Merge changes I5d7a6baa,Id0fb2d4e

* changes:
  wear_ui: Expose menu_unusable_rows via Makefile var.
  Allow customizing WearRecoveryUI via Makefile variables.
diff --git a/applypatch/Android.mk b/applypatch/Android.mk
index 7aed0a9..e38207c 100644
--- a/applypatch/Android.mk
+++ b/applypatch/Android.mk
@@ -151,7 +151,8 @@
 LOCAL_STATIC_LIBRARIES := \
     $(libimgdiff_static_libraries)
 LOCAL_C_INCLUDES := \
-    $(LOCAL_PATH)/include
+    $(LOCAL_PATH)/include \
+    bootable/recovery
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 include $(BUILD_STATIC_LIBRARY)
 
@@ -166,7 +167,8 @@
 LOCAL_STATIC_LIBRARIES := \
     $(libimgdiff_static_libraries)
 LOCAL_C_INCLUDES := \
-    $(LOCAL_PATH)/include
+    $(LOCAL_PATH)/include \
+    bootable/recovery
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 include $(BUILD_HOST_STATIC_LIBRARY)
 
@@ -180,4 +182,7 @@
     libimgdiff \
     $(libimgdiff_static_libraries) \
     libbz
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include \
+    bootable/recovery
 include $(BUILD_HOST_EXECUTABLE)
diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp
index 8802652..59b6007 100644
--- a/applypatch/imgdiff.cpp
+++ b/applypatch/imgdiff.cpp
@@ -140,11 +140,12 @@
 #include <android-base/logging.h>
 #include <android-base/memory.h>
 #include <android-base/unique_fd.h>
-#include <ziparchive/zip_archive.h>
-
 #include <bsdiff.h>
+#include <ziparchive/zip_archive.h>
 #include <zlib.h>
 
+#include "applypatch/imgdiff_image.h"
+
 using android::base::get_unaligned;
 
 static constexpr auto BUFFER_SIZE = 0x8000;
@@ -161,107 +162,16 @@
   return android::base::WriteFully(fd, &value, sizeof(int32_t));
 }
 
-class ImageChunk {
- public:
-  static constexpr auto WINDOWBITS = -15;  // 32kb window; negative to indicate a raw stream.
-  static constexpr auto MEMLEVEL = 8;      // the default value.
-  static constexpr auto METHOD = Z_DEFLATED;
-  static constexpr auto STRATEGY = Z_DEFAULT_STRATEGY;
-
-  ImageChunk(int type, size_t start, const std::vector<uint8_t>* file_content, size_t raw_data_len)
-      : type_(type),
-        start_(start),
-        input_file_ptr_(file_content),
-        raw_data_len_(raw_data_len),
-        compress_level_(6),
-        source_start_(0),
-        source_len_(0),
-        source_uncompressed_len_(0) {
-    CHECK(file_content != nullptr) << "input file container can't be nullptr";
-  }
-
-  int GetType() const {
-    return type_;
-  }
-  size_t GetRawDataLength() const {
-    return raw_data_len_;
-  }
-  const std::string& GetEntryName() const {
-    return entry_name_;
-  }
-
-  // CHUNK_DEFLATE will return the uncompressed data for diff, while other types will simply return
-  // the raw data.
-  const uint8_t * DataForPatch() const;
-  size_t DataLengthForPatch() const;
-
-  void Dump() const {
-    printf("type: %d, start: %zu, len: %zu, name: %s\n", type_, start_, DataLengthForPatch(),
-           entry_name_.c_str());
-  }
-
-  void SetSourceInfo(const ImageChunk& other);
-  void SetEntryName(std::string entryname);
-  void SetUncompressedData(std::vector<uint8_t> data);
-  bool SetBonusData(const std::vector<uint8_t>& bonus_data);
-
-  bool operator==(const ImageChunk& other) const;
-  bool operator!=(const ImageChunk& other) const {
-    return !(*this == other);
-  }
-
-  size_t GetHeaderSize(size_t patch_size) const;
-  // Return the offset of the next patch into the patch data.
-  size_t WriteHeaderToFd(int fd, const std::vector<uint8_t>& patch, size_t offset) const;
-
-  /*
-   * Cause a gzip chunk to be treated as a normal chunk (ie, as a blob
-   * of uninterpreted data).  The resulting patch will likely be about
-   * as big as the target file, but it lets us handle the case of images
-   * where some gzip chunks are reconstructible but others aren't (by
-   * treating the ones that aren't as normal chunks).
-   */
-  void ChangeDeflateChunkToNormal();
-  bool ChangeChunkToRaw(size_t patch_size);
-
-  /*
-   * Verify that we can reproduce exactly the same compressed data that
-   * we started with.  Sets the level, method, windowBits, memLevel, and
-   * strategy fields in the chunk to the encoding parameters needed to
-   * produce the right output.
-   */
-  bool ReconstructDeflateChunk();
-  bool IsAdjacentNormal(const ImageChunk& other) const;
-  void MergeAdjacentNormal(const ImageChunk& other);
-
-  /*
-   * Compute a bsdiff patch between |this| and the input source chunks.
-   * Store the result in the patch_data.
-   * |bsdiff_cache| can be used to cache the suffix array if the same |src| chunk is used
-   * repeatedly, pass nullptr if not needed.
-   */
-  bool MakePatch(const ImageChunk& src, std::vector<uint8_t>* patch_data, saidx_t** bsdiff_cache);
-
- private:
-  int type_;                                    // CHUNK_NORMAL, CHUNK_DEFLATE, CHUNK_RAW
-  size_t start_;                                // offset of chunk in the original input file
-  const std::vector<uint8_t>* input_file_ptr_;  // ptr to the full content of original input file
-  size_t raw_data_len_;
-
-  // --- for CHUNK_DEFLATE chunks only: ---
-  std::vector<uint8_t> uncompressed_data_;
-  std::string entry_name_;  // used for zip entries
-
-  // deflate encoder parameters
-  int compress_level_;
-
-  size_t source_start_;
-  size_t source_len_;
-  size_t source_uncompressed_len_;
-
-  const uint8_t* GetRawData() const;
-  bool TryReconstruction(int level);
-};
+ImageChunk::ImageChunk(int type, size_t start, const std::vector<uint8_t>* file_content,
+                       size_t raw_data_len, std::string entry_name)
+    : type_(type),
+      start_(start),
+      input_file_ptr_(file_content),
+      raw_data_len_(raw_data_len),
+      compress_level_(6),
+      entry_name_(std::move(entry_name)) {
+  CHECK(file_content != nullptr) << "input file container can't be nullptr";
+}
 
 const uint8_t* ImageChunk::GetRawData() const {
   CHECK_LE(start_ + raw_data_len_, input_file_ptr_->size());
@@ -290,20 +200,6 @@
           memcmp(GetRawData(), other.GetRawData(), raw_data_len_) == 0);
 }
 
-void ImageChunk::SetSourceInfo(const ImageChunk& src) {
-  source_start_ = src.start_;
-  if (type_ == CHUNK_NORMAL) {
-    source_len_ = src.raw_data_len_;
-  } else if (type_ == CHUNK_DEFLATE) {
-    source_len_ = src.raw_data_len_;
-    source_uncompressed_len_ = src.uncompressed_data_.size();
-  }
-}
-
-void ImageChunk::SetEntryName(std::string entryname) {
-  entry_name_ = std::move(entryname);
-}
-
 void ImageChunk::SetUncompressedData(std::vector<uint8_t> data) {
   uncompressed_data_ = std::move(data);
 }
@@ -316,18 +212,6 @@
   return true;
 }
 
-// Convert CHUNK_NORMAL & CHUNK_DEFLATE to CHUNK_RAW if the target size is
-// smaller. Also take the header size into account during size comparison.
-bool ImageChunk::ChangeChunkToRaw(size_t patch_size) {
-  if (type_ == CHUNK_RAW) {
-    return true;
-  } else if (type_ == CHUNK_NORMAL && (raw_data_len_ <= 160 || raw_data_len_ < patch_size)) {
-    type_ = CHUNK_RAW;
-    return true;
-  }
-  return false;
-}
-
 void ImageChunk::ChangeDeflateChunkToNormal() {
   if (type_ != CHUNK_DEFLATE) return;
   type_ = CHUNK_NORMAL;
@@ -335,61 +219,6 @@
   uncompressed_data_.clear();
 }
 
-// Header size:
-// header_type    4 bytes
-// CHUNK_NORMAL   8*3 = 24 bytes
-// CHUNK_DEFLATE  8*5 + 4*5 = 60 bytes
-// CHUNK_RAW      4 bytes + patch_size
-size_t ImageChunk::GetHeaderSize(size_t patch_size) const {
-  switch (type_) {
-    case CHUNK_NORMAL:
-      return 4 + 8 * 3;
-    case CHUNK_DEFLATE:
-      return 4 + 8 * 5 + 4 * 5;
-    case CHUNK_RAW:
-      return 4 + 4 + patch_size;
-    default:
-      CHECK(false) << "unexpected chunk type: " << type_;  // Should not reach here.
-      return 0;
-  }
-}
-
-size_t ImageChunk::WriteHeaderToFd(int fd, const std::vector<uint8_t>& patch, size_t offset) const {
-  Write4(fd, type_);
-  switch (type_) {
-    case CHUNK_NORMAL:
-      printf("normal   (%10zu, %10zu)  %10zu\n", start_, raw_data_len_, patch.size());
-      Write8(fd, static_cast<int64_t>(source_start_));
-      Write8(fd, static_cast<int64_t>(source_len_));
-      Write8(fd, static_cast<int64_t>(offset));
-      return offset + patch.size();
-    case CHUNK_DEFLATE:
-      printf("deflate  (%10zu, %10zu)  %10zu  %s\n", start_, raw_data_len_, patch.size(),
-             entry_name_.c_str());
-      Write8(fd, static_cast<int64_t>(source_start_));
-      Write8(fd, static_cast<int64_t>(source_len_));
-      Write8(fd, static_cast<int64_t>(offset));
-      Write8(fd, static_cast<int64_t>(source_uncompressed_len_));
-      Write8(fd, static_cast<int64_t>(uncompressed_data_.size()));
-      Write4(fd, compress_level_);
-      Write4(fd, METHOD);
-      Write4(fd, WINDOWBITS);
-      Write4(fd, MEMLEVEL);
-      Write4(fd, STRATEGY);
-      return offset + patch.size();
-    case CHUNK_RAW:
-      printf("raw      (%10zu, %10zu)\n", start_, raw_data_len_);
-      Write4(fd, static_cast<int32_t>(patch.size()));
-      if (!android::base::WriteFully(fd, patch.data(), patch.size())) {
-        CHECK(false) << "failed to write " << patch.size() <<" bytes patch";
-      }
-      return offset;
-    default:
-      CHECK(false) << "unexpected chunk type: " << type_;
-      return offset;
-  }
-}
-
 bool ImageChunk::IsAdjacentNormal(const ImageChunk& other) const {
   if (type_ != CHUNK_NORMAL || other.type_ != CHUNK_NORMAL) {
     return false;
@@ -402,15 +231,8 @@
   raw_data_len_ = raw_data_len_ + other.raw_data_len_;
 }
 
-bool ImageChunk::MakePatch(const ImageChunk& src, std::vector<uint8_t>* patch_data,
-                           saidx_t** bsdiff_cache) {
-  if (ChangeChunkToRaw(0)) {
-    size_t patch_size = DataLengthForPatch();
-    patch_data->resize(patch_size);
-    std::copy(DataForPatch(), DataForPatch() + patch_size, patch_data->begin());
-    return true;
-  }
-
+bool ImageChunk::MakePatch(const ImageChunk& tgt, const ImageChunk& src,
+                           std::vector<uint8_t>* patch_data, saidx_t** bsdiff_cache) {
 #if defined(__ANDROID__)
   char ptemp[] = "/data/local/tmp/imgdiff-patch-XXXXXX";
 #else
@@ -424,8 +246,8 @@
   }
   close(fd);
 
-  int r = bsdiff::bsdiff(src.DataForPatch(), src.DataLengthForPatch(), DataForPatch(),
-                         DataLengthForPatch(), ptemp, bsdiff_cache);
+  int r = bsdiff::bsdiff(src.DataForPatch(), src.DataLengthForPatch(), tgt.DataForPatch(),
+                         tgt.DataLengthForPatch(), ptemp, bsdiff_cache);
   if (r != 0) {
     printf("bsdiff() failed: %d\n", r);
     return false;
@@ -443,14 +265,7 @@
   }
 
   size_t sz = static_cast<size_t>(st.st_size);
-  // Change the chunk type to raw if the patch takes less space that way.
-  if (ChangeChunkToRaw(sz)) {
-    unlink(ptemp);
-    size_t patch_size = DataLengthForPatch();
-    patch_data->resize(patch_size);
-    std::copy(DataForPatch(), DataForPatch() + patch_size, patch_data->begin());
-    return true;
-  }
+
   patch_data->resize(sz);
   if (!android::base::ReadFully(patch_fd, patch_data->data(), sz)) {
     printf("failed to read \"%s\" %s\n", ptemp, strerror(errno));
@@ -459,7 +274,6 @@
   }
 
   unlink(ptemp);
-  SetSourceInfo(src);
 
   return true;
 }
@@ -470,8 +284,8 @@
     return false;
   }
 
-  // We only check two combinations of encoder parameters:  level 6
-  // (the default) and level 9 (the maximum).
+  // We only check two combinations of encoder parameters:  level 6 (the default) and level 9
+  // (the maximum).
   for (int level = 6; level <= 9; level += 3) {
     if (TryReconstruction(level)) {
       compress_level_ = level;
@@ -483,10 +297,9 @@
 }
 
 /*
- * Takes the uncompressed data stored in the chunk, compresses it
- * using the zlib parameters stored in the chunk, and checks that it
- * matches exactly the compressed data we started with (also stored in
- * the chunk).
+ * Takes the uncompressed data stored in the chunk, compresses it using the zlib parameters stored
+ * in the chunk, and checks that it matches exactly the compressed data we started with (also
+ * stored in the chunk).
  */
 bool ImageChunk::TryReconstruction(int level) {
   z_stream strm;
@@ -529,55 +342,136 @@
   return true;
 }
 
-// Interface for zip_mode and image_mode images. We initialize the image from an input file and
-// split the file content into a list of image chunks.
-class Image {
- public:
-  explicit Image(bool is_source) : is_source_(is_source) {}
+PatchChunk::PatchChunk(const ImageChunk& tgt, const ImageChunk& src, std::vector<uint8_t> data)
+    : type_(tgt.GetType()),
+      source_start_(src.GetStartOffset()),
+      source_len_(src.GetRawDataLength()),
+      source_uncompressed_len_(src.DataLengthForPatch()),
+      target_start_(tgt.GetStartOffset()),
+      target_len_(tgt.GetRawDataLength()),
+      target_uncompressed_len_(tgt.DataLengthForPatch()),
+      target_compress_level_(tgt.GetCompressLevel()),
+      data_(std::move(data)) {}
 
-  virtual ~Image() {}
+// Construct a CHUNK_RAW patch from the target data directly.
+PatchChunk::PatchChunk(const ImageChunk& tgt)
+    : type_(CHUNK_RAW),
+      source_start_(0),
+      source_len_(0),
+      source_uncompressed_len_(0),
+      target_start_(tgt.GetStartOffset()),
+      target_len_(tgt.GetRawDataLength()),
+      target_uncompressed_len_(tgt.DataLengthForPatch()),
+      target_compress_level_(tgt.GetCompressLevel()),
+      data_(tgt.DataForPatch(), tgt.DataForPatch() + tgt.DataLengthForPatch()) {}
 
-  // Create a list of image chunks from input file.
-  virtual bool Initialize(const std::string& filename) = 0;
+// Return true if raw data is smaller than the patch size.
+bool PatchChunk::RawDataIsSmaller(const ImageChunk& tgt, size_t patch_size) {
+  size_t target_len = tgt.GetRawDataLength();
+  return (tgt.GetType() == CHUNK_NORMAL && (target_len <= 160 || target_len < patch_size));
+}
 
-  // Look for runs of adjacent normal chunks and compress them down into a single chunk.  (Such
-  // runs can be produced when deflate chunks are changed to normal chunks.)
-  void MergeAdjacentNormalChunks();
+// Header size:
+// header_type    4 bytes
+// CHUNK_NORMAL   8*3 = 24 bytes
+// CHUNK_DEFLATE  8*5 + 4*5 = 60 bytes
+// CHUNK_RAW      4 bytes + patch_size
+size_t PatchChunk::GetHeaderSize() const {
+  switch (type_) {
+    case CHUNK_NORMAL:
+      return 4 + 8 * 3;
+    case CHUNK_DEFLATE:
+      return 4 + 8 * 5 + 4 * 5;
+    case CHUNK_RAW:
+      return 4 + 4 + data_.size();
+    default:
+      CHECK(false) << "unexpected chunk type: " << type_;  // Should not reach here.
+      return 0;
+  }
+}
 
-  // In zip mode, find the matching deflate source chunk by entry name. Search for normal chunks
-  // also if |find_normal| is true.
-  ImageChunk* FindChunkByName(const std::string& name, bool find_normal = false);
+// Return the offset of the next patch into the patch data.
+size_t PatchChunk::WriteHeaderToFd(int fd, size_t offset) const {
+  Write4(fd, type_);
+  switch (type_) {
+    case CHUNK_NORMAL:
+      printf("normal   (%10zu, %10zu)  %10zu\n", target_start_, target_len_, data_.size());
+      Write8(fd, static_cast<int64_t>(source_start_));
+      Write8(fd, static_cast<int64_t>(source_len_));
+      Write8(fd, static_cast<int64_t>(offset));
+      return offset + data_.size();
+    case CHUNK_DEFLATE:
+      printf("deflate  (%10zu, %10zu)  %10zu\n", target_start_, target_len_, data_.size());
+      Write8(fd, static_cast<int64_t>(source_start_));
+      Write8(fd, static_cast<int64_t>(source_len_));
+      Write8(fd, static_cast<int64_t>(offset));
+      Write8(fd, static_cast<int64_t>(source_uncompressed_len_));
+      Write8(fd, static_cast<int64_t>(target_uncompressed_len_));
+      Write4(fd, target_compress_level_);
+      Write4(fd, ImageChunk::METHOD);
+      Write4(fd, ImageChunk::WINDOWBITS);
+      Write4(fd, ImageChunk::MEMLEVEL);
+      Write4(fd, ImageChunk::STRATEGY);
+      return offset + data_.size();
+    case CHUNK_RAW:
+      printf("raw      (%10zu, %10zu)\n", target_start_, target_len_);
+      Write4(fd, static_cast<int32_t>(data_.size()));
+      if (!android::base::WriteFully(fd, data_.data(), data_.size())) {
+        CHECK(false) << "failed to write " << data_.size() << " bytes patch";
+      }
+      return offset;
+    default:
+      CHECK(false) << "unexpected chunk type: " << type_;
+      return offset;
+  }
+}
 
-  // Write the contents of |patch_data| to |patch_fd|.
-  bool WritePatchDataToFd(const std::vector<std::vector<uint8_t>>& patch_data, int patch_fd) const;
-
-  void DumpChunks() const;
-
-  // Non const iterators to access the stored ImageChunks.
-  std::vector<ImageChunk>::iterator begin() {
-    return chunks_.begin();
+// Write the contents of |patch_chunks| to |patch_fd|.
+bool PatchChunk::WritePatchDataToFd(const std::vector<PatchChunk>& patch_chunks, int patch_fd) {
+  // Figure out how big the imgdiff file header is going to be, so that we can correctly compute
+  // the offset of each bsdiff patch within the file.
+  size_t total_header_size = 12;
+  for (const auto& patch : patch_chunks) {
+    total_header_size += patch.GetHeaderSize();
   }
 
-  std::vector<ImageChunk>::iterator end() {
-    return chunks_.end();
-  }
-  // Return a pointer to the ith ImageChunk.
-  ImageChunk* Get(size_t i) {
-    CHECK_LT(i, chunks_.size());
-    return &chunks_[i];
+  size_t offset = total_header_size;
+
+  // Write out the headers.
+  if (!android::base::WriteStringToFd("IMGDIFF2", patch_fd)) {
+    printf("failed to write \"IMGDIFF2\": %s\n", strerror(errno));
+    return false;
   }
 
-  size_t NumOfChunks() const {
-    return chunks_.size();
+  Write4(patch_fd, static_cast<int32_t>(patch_chunks.size()));
+  for (size_t i = 0; i < patch_chunks.size(); ++i) {
+    printf("chunk %zu: ", i);
+    offset = patch_chunks[i].WriteHeaderToFd(patch_fd, offset);
   }
 
- protected:
-  bool ReadFile(const std::string& filename, std::vector<uint8_t>* file_content);
+  // Append each chunk's bsdiff patch, in order.
+  for (const auto& patch : patch_chunks) {
+    if (patch.type_ == CHUNK_RAW) {
+      continue;
+    }
+    if (!android::base::WriteFully(patch_fd, patch.data_.data(), patch.data_.size())) {
+      printf("failed to write %zu bytes patch to patch_fd\n", patch.data_.size());
+      return false;
+    }
+  }
 
-  bool is_source_;                     // True if it's for source chunks.
-  std::vector<ImageChunk> chunks_;     // Internal storage of ImageChunk.
-  std::vector<uint8_t> file_content_;  // Store the whole input file in memory.
-};
+  return true;
+}
+
+ImageChunk& Image::operator[](size_t i) {
+  CHECK_LT(i, chunks_.size());
+  return chunks_[i];
+}
+
+const ImageChunk& Image::operator[](size_t i) const {
+  CHECK_LT(i, chunks_.size());
+  return chunks_[i];
+}
 
 void Image::MergeAdjacentNormalChunks() {
   size_t merged_last = 0, cur = 0;
@@ -601,54 +495,6 @@
   }
 }
 
-ImageChunk* Image::FindChunkByName(const std::string& name, bool find_normal) {
-  if (name.empty()) {
-    return nullptr;
-  }
-  for (auto& chunk : chunks_) {
-    if ((chunk.GetType() == CHUNK_DEFLATE || find_normal) && chunk.GetEntryName() == name) {
-      return &chunk;
-    }
-  }
-  return nullptr;
-}
-
-bool Image::WritePatchDataToFd(const std::vector<std::vector<uint8_t>>& patch_data,
-                               int patch_fd) const {
-  // Figure out how big the imgdiff file header is going to be, so that we can correctly compute
-  // the offset of each bsdiff patch within the file.
-  CHECK_EQ(chunks_.size(), patch_data.size());
-  size_t total_header_size = 12;
-  for (size_t i = 0; i < chunks_.size(); ++i) {
-    total_header_size += chunks_[i].GetHeaderSize(patch_data[i].size());
-  }
-
-  size_t offset = total_header_size;
-
-  // Write out the headers.
-  if (!android::base::WriteStringToFd("IMGDIFF2", patch_fd)) {
-    printf("failed to write \"IMGDIFF2\": %s\n", strerror(errno));
-    return false;
-  }
-  Write4(patch_fd, static_cast<int32_t>(chunks_.size()));
-  for (size_t i = 0; i < chunks_.size(); ++i) {
-    printf("chunk %zu: ", i);
-    offset = chunks_[i].WriteHeaderToFd(patch_fd, patch_data[i], offset);
-  }
-
-  // Append each chunk's bsdiff patch, in order.
-  for (size_t i = 0; i < chunks_.size(); ++i) {
-    if (chunks_[i].GetType() != CHUNK_RAW) {
-      if (!android::base::WriteFully(patch_fd, patch_data[i].data(), patch_data[i].size())) {
-        printf("failed to write %zu bytes patch for chunk %zu\n", patch_data[i].size(), i);
-        return false;
-      }
-    }
-  }
-
-  return true;
-}
-
 void Image::DumpChunks() const {
   std::string type = is_source_ ? "source" : "target";
   printf("Dumping chunks for %s\n", type.c_str());
@@ -683,39 +529,6 @@
   return true;
 }
 
-class ZipModeImage : public Image {
- public:
-  explicit ZipModeImage(bool is_source) : Image(is_source) {}
-
-  bool Initialize(const std::string& filename) override;
-
-  const ImageChunk& PseudoSource() const {
-    CHECK(is_source_);
-    CHECK(pseudo_source_ != nullptr);
-    return *pseudo_source_;
-  }
-
-  // Verify that we can reconstruct the deflate chunks; also change the type to CHUNK_NORMAL if
-  // src and tgt are identical.
-  static bool CheckAndProcessChunks(ZipModeImage* tgt_image, ZipModeImage* src_image);
-
-  // Compute the patches against the input image, and write the data into |patch_name|.
-  static bool GeneratePatches(ZipModeImage* tgt_image, ZipModeImage* src_image,
-                              const std::string& patch_name);
-
- private:
-  // Initialize image chunks based on the zip entries.
-  bool InitializeChunks(const std::string& filename, ZipArchiveHandle handle);
-  // Add the a zip entry to the list.
-  bool AddZipEntryToChunks(ZipArchiveHandle handle, const std::string& entry_name, ZipEntry* entry);
-  // Return the real size of the zip file. (omit the trailing zeros that used for alignment)
-  bool GetZipFileSize(size_t* input_file_size);
-
-  // The pesudo source chunk for bsdiff if there's no match for the given target chunk. It's in
-  // fact the whole source file.
-  std::unique_ptr<ImageChunk> pseudo_source_;
-};
-
 bool ZipModeImage::Initialize(const std::string& filename) {
   if (!ReadFile(filename, &file_content_)) {
     return false;
@@ -736,9 +549,6 @@
     return false;
   }
 
-  if (is_source_) {
-    pseudo_source_ = std::make_unique<ImageChunk>(CHUNK_NORMAL, 0, &file_content_, zipfile_size);
-  }
   if (!InitializeChunks(filename, handle)) {
     CloseArchive(handle);
     return false;
@@ -834,14 +644,11 @@
              ErrorCodeString(ret));
       return false;
     }
-    ImageChunk curr(CHUNK_DEFLATE, entry->offset, &file_content_, compressed_len);
-    curr.SetEntryName(entry_name);
+    ImageChunk curr(CHUNK_DEFLATE, entry->offset, &file_content_, compressed_len, entry_name);
     curr.SetUncompressedData(std::move(uncompressed_data));
-    chunks_.push_back(curr);
+    chunks_.push_back(std::move(curr));
   } else {
-    ImageChunk curr(CHUNK_NORMAL, entry->offset, &file_content_, compressed_len);
-    curr.SetEntryName(entry_name);
-    chunks_.push_back(curr);
+    chunks_.emplace_back(CHUNK_NORMAL, entry->offset, &file_content_, compressed_len, entry_name);
   }
 
   return true;
@@ -880,6 +687,28 @@
   return false;
 }
 
+ImageChunk ZipModeImage::PseudoSource() const {
+  CHECK(is_source_);
+  return ImageChunk(CHUNK_NORMAL, 0, &file_content_, file_content_.size());
+}
+
+const ImageChunk* ZipModeImage::FindChunkByName(const std::string& name, bool find_normal) const {
+  if (name.empty()) {
+    return nullptr;
+  }
+  for (auto& chunk : chunks_) {
+    if ((chunk.GetType() == CHUNK_DEFLATE || find_normal) && chunk.GetEntryName() == name) {
+      return &chunk;
+    }
+  }
+  return nullptr;
+}
+
+ImageChunk* ZipModeImage::FindChunkByName(const std::string& name, bool find_normal) {
+  return const_cast<ImageChunk*>(
+      static_cast<const ZipModeImage*>(this)->FindChunkByName(name, find_normal));
+}
+
 bool ZipModeImage::CheckAndProcessChunks(ZipModeImage* tgt_image, ZipModeImage* src_image) {
   for (auto& tgt_chunk : *tgt_image) {
     if (tgt_chunk.GetType() != CHUNK_DEFLATE) {
@@ -907,40 +736,55 @@
     }
   }
 
-  return true;
-}
-
-bool ZipModeImage::GeneratePatches(ZipModeImage* tgt_image, ZipModeImage* src_image,
-                                   const std::string& patch_name) {
   // For zips, we only need merge normal chunks for the target:  deflated chunks are matched via
   // filename, and normal chunks are patched using the entire source file as the source.
   tgt_image->MergeAdjacentNormalChunks();
   tgt_image->DumpChunks();
 
-  printf("Construct patches for %zu chunks...\n", tgt_image->NumOfChunks());
-  std::vector<std::vector<uint8_t>> patch_data(tgt_image->NumOfChunks());
+  return true;
+}
+
+bool ZipModeImage::GeneratePatches(const ZipModeImage& tgt_image, const ZipModeImage& src_image,
+                                   const std::string& patch_name) {
+  printf("Construct patches for %zu chunks...\n", tgt_image.NumOfChunks());
+  std::vector<PatchChunk> patch_chunks;
+  patch_chunks.reserve(tgt_image.NumOfChunks());
 
   saidx_t* bsdiff_cache = nullptr;
-  size_t i = 0;
-  for (auto& tgt_chunk : *tgt_image) {
-    ImageChunk* src_chunk = (tgt_chunk.GetType() != CHUNK_DEFLATE)
-                                ? nullptr
-                                : src_image->FindChunkByName(tgt_chunk.GetEntryName());
+  for (size_t i = 0; i < tgt_image.NumOfChunks(); i++) {
+    const auto& tgt_chunk = tgt_image[i];
 
-    const auto& src_ref = (src_chunk == nullptr) ? src_image->PseudoSource() : *src_chunk;
+    if (PatchChunk::RawDataIsSmaller(tgt_chunk, 0)) {
+      patch_chunks.emplace_back(tgt_chunk);
+      continue;
+    }
+
+    const ImageChunk* src_chunk = (tgt_chunk.GetType() != CHUNK_DEFLATE)
+                                      ? nullptr
+                                      : src_image.FindChunkByName(tgt_chunk.GetEntryName());
+
+    const auto& src_ref = (src_chunk == nullptr) ? src_image.PseudoSource() : *src_chunk;
     saidx_t** bsdiff_cache_ptr = (src_chunk == nullptr) ? &bsdiff_cache : nullptr;
 
-    if (!tgt_chunk.MakePatch(src_ref, &patch_data[i], bsdiff_cache_ptr)) {
+    std::vector<uint8_t> patch_data;
+    if (!ImageChunk::MakePatch(tgt_chunk, src_ref, &patch_data, bsdiff_cache_ptr)) {
       printf("Failed to generate patch, name: %s\n", tgt_chunk.GetEntryName().c_str());
       return false;
     }
 
-    printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data[i].size(),
+    printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data.size(),
            tgt_chunk.GetRawDataLength());
-    i++;
+
+    if (PatchChunk::RawDataIsSmaller(tgt_chunk, patch_data.size())) {
+      patch_chunks.emplace_back(tgt_chunk);
+    } else {
+      patch_chunks.emplace_back(tgt_chunk, src_ref, std::move(patch_data));
+    }
   }
   free(bsdiff_cache);
 
+  CHECK_EQ(tgt_image.NumOfChunks(), patch_chunks.size());
+
   android::base::unique_fd patch_fd(
       open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR));
   if (patch_fd == -1) {
@@ -948,26 +792,9 @@
     return false;
   }
 
-  return tgt_image->WritePatchDataToFd(patch_data, patch_fd);
+  return PatchChunk::WritePatchDataToFd(patch_chunks, patch_fd);
 }
 
-class ImageModeImage : public Image {
- public:
-  explicit ImageModeImage(bool is_source) : Image(is_source) {}
-
-  // Initialize the image chunks list by searching the magic numbers in an image file.
-  bool Initialize(const std::string& filename) override;
-
-  // In Image Mode, verify that the source and target images have the same chunk structure (ie, the
-  // same sequence of deflate and normal chunks).
-  static bool CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeImage* src_image);
-
-  // In image mode, generate patches against the given source chunks and bonus_data; write the
-  // result to |patch_name|.
-  static bool GeneratePatches(ImageModeImage* tgt_image, ImageModeImage* src_image,
-                              const std::vector<uint8_t>& bonus_data, const std::string& patch_name);
-};
-
 bool ImageModeImage::Initialize(const std::string& filename) {
   if (!ReadFile(filename, &file_content_)) {
     return false;
@@ -1053,7 +880,7 @@
       ImageChunk body(CHUNK_DEFLATE, pos, &file_content_, raw_data_len);
       uncompressed_data.resize(uncompressed_len);
       body.SetUncompressedData(std::move(uncompressed_data));
-      chunks_.push_back(body);
+      chunks_.push_back(std::move(body));
 
       pos += raw_data_len;
 
@@ -1083,6 +910,18 @@
   return true;
 }
 
+bool ImageModeImage::SetBonusData(const std::vector<uint8_t>& bonus_data) {
+  CHECK(is_source_);
+  if (chunks_.size() < 2 || !chunks_[1].SetBonusData(bonus_data)) {
+    printf("Failed to set bonus data\n");
+    DumpChunks();
+    return false;
+  }
+
+  printf("  using %zu bytes of bonus data\n", bonus_data.size());
+  return true;
+}
+
 // In Image Mode, verify that the source and target images have the same chunk structure (ie, the
 // same sequence of deflate and normal chunks).
 bool ImageModeImage::CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeImage* src_image) {
@@ -1097,7 +936,7 @@
     return false;
   }
   for (size_t i = 0; i < tgt_image->NumOfChunks(); ++i) {
-    if (tgt_image->Get(i)->GetType() != src_image->Get(i)->GetType()) {
+    if ((*tgt_image)[i].GetType() != (*src_image)[i].GetType()) {
       printf("source and target don't have same chunk structure! (chunk %zu)\n", i);
       tgt_image->DumpChunks();
       src_image->DumpChunks();
@@ -1106,26 +945,23 @@
   }
 
   for (size_t i = 0; i < tgt_image->NumOfChunks(); ++i) {
-    auto& tgt_chunk = *tgt_image->Get(i);
-    auto& src_chunk = *src_image->Get(i);
+    auto& tgt_chunk = (*tgt_image)[i];
+    auto& src_chunk = (*src_image)[i];
     if (tgt_chunk.GetType() != CHUNK_DEFLATE) {
       continue;
     }
 
-    // Confirm that we can recompress the data and get exactly the same bits as are in the
-    // input target image.
-    if (!tgt_chunk.ReconstructDeflateChunk()) {
-      printf("failed to reconstruct target deflate chunk %zu [%s]; treating as normal\n", i,
-             tgt_chunk.GetEntryName().c_str());
-      tgt_chunk.ChangeDeflateChunkToNormal();
-      src_chunk.ChangeDeflateChunkToNormal();
-      continue;
-    }
-
     // If two deflate chunks are identical treat them as normal chunks.
     if (tgt_chunk == src_chunk) {
       tgt_chunk.ChangeDeflateChunkToNormal();
       src_chunk.ChangeDeflateChunkToNormal();
+    } else if (!tgt_chunk.ReconstructDeflateChunk()) {
+      // We cannot recompress the data and get exactly the same bits as are in the input target
+      // image, fall back to normal
+      printf("failed to reconstruct target deflate chunk %zu [%s]; treating as normal\n", i,
+             tgt_chunk.GetEntryName().c_str());
+      tgt_chunk.ChangeDeflateChunkToNormal();
+      src_chunk.ChangeDeflateChunkToNormal();
     }
   }
 
@@ -1144,29 +980,39 @@
 
 // In image mode, generate patches against the given source chunks and bonus_data; write the
 // result to |patch_name|.
-bool ImageModeImage::GeneratePatches(ImageModeImage* tgt_image, ImageModeImage* src_image,
-                                     const std::vector<uint8_t>& bonus_data,
+bool ImageModeImage::GeneratePatches(const ImageModeImage& tgt_image,
+                                     const ImageModeImage& src_image,
                                      const std::string& patch_name) {
-  printf("Construct patches for %zu chunks...\n", tgt_image->NumOfChunks());
-  std::vector<std::vector<uint8_t>> patch_data(tgt_image->NumOfChunks());
+  printf("Construct patches for %zu chunks...\n", tgt_image.NumOfChunks());
+  std::vector<PatchChunk> patch_chunks;
+  patch_chunks.reserve(tgt_image.NumOfChunks());
 
-  for (size_t i = 0; i < tgt_image->NumOfChunks(); i++) {
-    auto& tgt_chunk = *tgt_image->Get(i);
-    auto& src_chunk = *src_image->Get(i);
+  for (size_t i = 0; i < tgt_image.NumOfChunks(); i++) {
+    const auto& tgt_chunk = tgt_image[i];
+    const auto& src_chunk = src_image[i];
 
-    if (i == 1 && !bonus_data.empty()) {
-      printf("  using %zu bytes of bonus data for chunk %zu\n", bonus_data.size(), i);
-      src_chunk.SetBonusData(bonus_data);
+    if (PatchChunk::RawDataIsSmaller(tgt_chunk, 0)) {
+      patch_chunks.emplace_back(tgt_chunk);
+      continue;
     }
 
-    if (!tgt_chunk.MakePatch(src_chunk, &patch_data[i], nullptr)) {
+    std::vector<uint8_t> patch_data;
+    if (!ImageChunk::MakePatch(tgt_chunk, src_chunk, &patch_data, nullptr)) {
       printf("Failed to generate patch for target chunk %zu: ", i);
       return false;
     }
-    printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data[i].size(),
+    printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data.size(),
            tgt_chunk.GetRawDataLength());
+
+    if (PatchChunk::RawDataIsSmaller(tgt_chunk, patch_data.size())) {
+      patch_chunks.emplace_back(tgt_chunk);
+    } else {
+      patch_chunks.emplace_back(tgt_chunk, src_chunk, std::move(patch_data));
+    }
   }
 
+  CHECK_EQ(tgt_image.NumOfChunks(), patch_chunks.size());
+
   android::base::unique_fd patch_fd(
       open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR));
   if (patch_fd == -1) {
@@ -1174,7 +1020,7 @@
     return false;
   }
 
-  return tgt_image->WritePatchDataToFd(patch_data, patch_fd);
+  return PatchChunk::WritePatchDataToFd(patch_chunks, patch_fd);
 }
 
 int imgdiff(int argc, const char** argv) {
@@ -1236,7 +1082,7 @@
     }
     // Compute bsdiff patches for each chunk's data (the uncompressed data, in the case of
     // deflate chunks).
-    if (!ZipModeImage::GeneratePatches(&tgt_image, &src_image, argv[optind + 2])) {
+    if (!ZipModeImage::GeneratePatches(tgt_image, src_image, argv[optind + 2])) {
       return 1;
     }
   } else {
@@ -1253,7 +1099,12 @@
     if (!ImageModeImage::CheckAndProcessChunks(&tgt_image, &src_image)) {
       return 1;
     }
-    if (!ImageModeImage::GeneratePatches(&tgt_image, &src_image, bonus_data, argv[optind + 2])) {
+
+    if (!bonus_data.empty() && !src_image.SetBonusData(bonus_data)) {
+      return 1;
+    }
+
+    if (!ImageModeImage::GeneratePatches(tgt_image, src_image, argv[optind + 2])) {
       return 1;
     }
   }
diff --git a/applypatch/include/applypatch/imgdiff_image.h b/applypatch/include/applypatch/imgdiff_image.h
new file mode 100644
index 0000000..221dd5a
--- /dev/null
+++ b/applypatch/include/applypatch/imgdiff_image.h
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _APPLYPATCH_IMGDIFF_IMAGE_H
+#define _APPLYPATCH_IMGDIFF_IMAGE_H
+
+#include <stddef.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include <bsdiff.h>
+#include <ziparchive/zip_archive.h>
+#include <zlib.h>
+
+#include "imgdiff.h"
+#include "rangeset.h"
+
+class ImageChunk {
+ public:
+  static constexpr auto WINDOWBITS = -15;  // 32kb window; negative to indicate a raw stream.
+  static constexpr auto MEMLEVEL = 8;      // the default value.
+  static constexpr auto METHOD = Z_DEFLATED;
+  static constexpr auto STRATEGY = Z_DEFAULT_STRATEGY;
+
+  ImageChunk(int type, size_t start, const std::vector<uint8_t>* file_content, size_t raw_data_len,
+             std::string entry_name = {});
+
+  int GetType() const {
+    return type_;
+  }
+  size_t GetRawDataLength() const {
+    return raw_data_len_;
+  }
+  const std::string& GetEntryName() const {
+    return entry_name_;
+  }
+  size_t GetStartOffset() const {
+    return start_;
+  }
+  int GetCompressLevel() const {
+    return compress_level_;
+  }
+
+  // CHUNK_DEFLATE will return the uncompressed data for diff, while other types will simply return
+  // the raw data.
+  const uint8_t* DataForPatch() const;
+  size_t DataLengthForPatch() const;
+
+  void Dump() const {
+    printf("type: %d, start: %zu, len: %zu, name: %s\n", type_, start_, DataLengthForPatch(),
+           entry_name_.c_str());
+  }
+
+  void SetUncompressedData(std::vector<uint8_t> data);
+  bool SetBonusData(const std::vector<uint8_t>& bonus_data);
+
+  bool operator==(const ImageChunk& other) const;
+  bool operator!=(const ImageChunk& other) const {
+    return !(*this == other);
+  }
+
+  /*
+   * Cause a gzip chunk to be treated as a normal chunk (ie, as a blob of uninterpreted data).
+   * The resulting patch will likely be about as big as the target file, but it lets us handle
+   * the case of images where some gzip chunks are reconstructible but others aren't (by treating
+   * the ones that aren't as normal chunks).
+   */
+  void ChangeDeflateChunkToNormal();
+
+  /*
+   * Verify that we can reproduce exactly the same compressed data that we started with.  Sets the
+   * level, method, windowBits, memLevel, and strategy fields in the chunk to the encoding
+   * parameters needed to produce the right output.
+   */
+  bool ReconstructDeflateChunk();
+  bool IsAdjacentNormal(const ImageChunk& other) const;
+  void MergeAdjacentNormal(const ImageChunk& other);
+
+  /*
+   * Compute a bsdiff patch between |src| and |tgt|; Store the result in the patch_data.
+   * |bsdiff_cache| can be used to cache the suffix array if the same |src| chunk is used
+   * repeatedly, pass nullptr if not needed.
+   */
+  static bool MakePatch(const ImageChunk& tgt, const ImageChunk& src,
+                        std::vector<uint8_t>* patch_data, saidx_t** bsdiff_cache);
+
+ private:
+  const uint8_t* GetRawData() const;
+  bool TryReconstruction(int level);
+
+  int type_;                                    // CHUNK_NORMAL, CHUNK_DEFLATE, CHUNK_RAW
+  size_t start_;                                // offset of chunk in the original input file
+  const std::vector<uint8_t>* input_file_ptr_;  // ptr to the full content of original input file
+  size_t raw_data_len_;
+
+  // deflate encoder parameters
+  int compress_level_;
+
+  // --- for CHUNK_DEFLATE chunks only: ---
+  std::vector<uint8_t> uncompressed_data_;
+  std::string entry_name_;  // used for zip entries
+};
+
+// PatchChunk stores the patch data between a source chunk and a target chunk. It also keeps track
+// of the metadata of src&tgt chunks (e.g. offset, raw data length, uncompressed data length).
+class PatchChunk {
+ public:
+  PatchChunk(const ImageChunk& tgt, const ImageChunk& src, std::vector<uint8_t> data);
+
+  // Construct a CHUNK_RAW patch from the target data directly.
+  explicit PatchChunk(const ImageChunk& tgt);
+
+  // Return true if raw data size is smaller than the patch size.
+  static bool RawDataIsSmaller(const ImageChunk& tgt, size_t patch_size);
+
+  static bool WritePatchDataToFd(const std::vector<PatchChunk>& patch_chunks, int patch_fd);
+
+ private:
+  size_t GetHeaderSize() const;
+  size_t WriteHeaderToFd(int fd, size_t offset) const;
+
+  // The patch chunk type is the same as the target chunk type. The only exception is we change
+  // the |type_| to CHUNK_RAW if target length is smaller than the patch size.
+  int type_;
+
+  size_t source_start_;
+  size_t source_len_;
+  size_t source_uncompressed_len_;
+
+  size_t target_start_;  // offset of the target chunk within the target file
+  size_t target_len_;
+  size_t target_uncompressed_len_;
+  size_t target_compress_level_;  // the deflate compression level of the target chunk.
+
+  std::vector<uint8_t> data_;  // storage for the patch data
+};
+
+// Interface for zip_mode and image_mode images. We initialize the image from an input file and
+// split the file content into a list of image chunks.
+class Image {
+ public:
+  explicit Image(bool is_source) : is_source_(is_source) {}
+
+  virtual ~Image() {}
+
+  // Create a list of image chunks from input file.
+  virtual bool Initialize(const std::string& filename) = 0;
+
+  // Look for runs of adjacent normal chunks and compress them down into a single chunk.  (Such
+  // runs can be produced when deflate chunks are changed to normal chunks.)
+  void MergeAdjacentNormalChunks();
+
+  void DumpChunks() const;
+
+  // Non const iterators to access the stored ImageChunks.
+  std::vector<ImageChunk>::iterator begin() {
+    return chunks_.begin();
+  }
+
+  std::vector<ImageChunk>::iterator end() {
+    return chunks_.end();
+  }
+
+  ImageChunk& operator[](size_t i);
+  const ImageChunk& operator[](size_t i) const;
+
+  size_t NumOfChunks() const {
+    return chunks_.size();
+  }
+
+ protected:
+  bool ReadFile(const std::string& filename, std::vector<uint8_t>* file_content);
+
+  bool is_source_;                     // True if it's for source chunks.
+  std::vector<ImageChunk> chunks_;     // Internal storage of ImageChunk.
+  std::vector<uint8_t> file_content_;  // Store the whole input file in memory.
+};
+
+class ZipModeImage : public Image {
+ public:
+  explicit ZipModeImage(bool is_source) : Image(is_source) {}
+
+  bool Initialize(const std::string& filename) override;
+
+  // The pesudo source chunk for bsdiff if there's no match for the given target chunk. It's in
+  // fact the whole source file.
+  ImageChunk PseudoSource() const;
+
+  // Find the matching deflate source chunk by entry name. Search for normal chunks also if
+  // |find_normal| is true.
+  ImageChunk* FindChunkByName(const std::string& name, bool find_normal = false);
+
+  const ImageChunk* FindChunkByName(const std::string& name, bool find_normal = false) const;
+
+  // Verify that we can reconstruct the deflate chunks; also change the type to CHUNK_NORMAL if
+  // src and tgt are identical.
+  static bool CheckAndProcessChunks(ZipModeImage* tgt_image, ZipModeImage* src_image);
+
+  // Compute the patch between tgt & src images, and write the data into |patch_name|.
+  static bool GeneratePatches(const ZipModeImage& tgt_image, const ZipModeImage& src_image,
+                              const std::string& patch_name);
+
+ private:
+  // Initialize image chunks based on the zip entries.
+  bool InitializeChunks(const std::string& filename, ZipArchiveHandle handle);
+  // Add the a zip entry to the list.
+  bool AddZipEntryToChunks(ZipArchiveHandle handle, const std::string& entry_name, ZipEntry* entry);
+  // Return the real size of the zip file. (omit the trailing zeros that used for alignment)
+  bool GetZipFileSize(size_t* input_file_size);
+};
+
+class ImageModeImage : public Image {
+ public:
+  explicit ImageModeImage(bool is_source) : Image(is_source) {}
+
+  // Initialize the image chunks list by searching the magic numbers in an image file.
+  bool Initialize(const std::string& filename) override;
+
+  bool SetBonusData(const std::vector<uint8_t>& bonus_data);
+
+  // In Image Mode, verify that the source and target images have the same chunk structure (ie, the
+  // same sequence of deflate and normal chunks).
+  static bool CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeImage* src_image);
+
+  // In image mode, generate patches against the given source chunks and bonus_data; write the
+  // result to |patch_name|.
+  static bool GeneratePatches(const ImageModeImage& tgt_image, const ImageModeImage& src_image,
+                              const std::string& patch_name);
+};
+
+#endif  // _APPLYPATCH_IMGDIFF_IMAGE_H
diff --git a/etc/init.rc b/etc/init.rc
index 2e3c7a7..d8121cc 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -79,9 +79,9 @@
     critical
     seclabel u:r:ueventd:s0
 
-service healthd /sbin/healthd -r
+service charger /charger -r
     critical
-    seclabel u:r:healthd:s0
+    seclabel u:r:charger:s0
 
 service recovery /sbin/recovery
     seclabel u:r:recovery:s0
diff --git a/minui/graphics.cpp b/minui/graphics.cpp
index 3bdc33f..3bfce11 100644
--- a/minui/graphics.cpp
+++ b/minui/graphics.cpp
@@ -258,7 +258,7 @@
 }
 
 int gr_init_font(const char* name, GRFont** dest) {
-    GRFont* font = reinterpret_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
+    GRFont* font = static_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
     if (font == nullptr) {
         return -1;
     }
@@ -291,7 +291,7 @@
 
 
     // fall back to the compiled-in font.
-    gr_font = static_cast<GRFont*>(calloc(sizeof(*gr_font), 1));
+    gr_font = static_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
     gr_font->texture = static_cast<GRSurface*>(malloc(sizeof(*gr_font->texture)));
     gr_font->texture->width = font.width;
     gr_font->texture->height = font.height;
diff --git a/updater/include/updater/rangeset.h b/rangeset.h
similarity index 96%
rename from updater/include/updater/rangeset.h
rename to rangeset.h
index b67c987..f224a08 100644
--- a/updater/include/updater/rangeset.h
+++ b/rangeset.h
@@ -266,13 +266,13 @@
         new_block_start += (old_block_start - range.first);
         return (new_block_start * kBlockSize + old_offset % kBlockSize);
       } else {
-        CHECK(false) <<"block_start " << old_block_start << " is missing between two ranges: "
-                     << this->ToString();
+        CHECK(false) << "block_start " << old_block_start
+                     << " is missing between two ranges: " << this->ToString();
         return 0;
       }
     }
-    CHECK(false) <<"block_start " << old_block_start << " exceeds the limit of current RangeSet: "
-                 << this->ToString();
+    CHECK(false) << "block_start " << old_block_start
+                 << " exceeds the limit of current RangeSet: " << this->ToString();
     return 0;
   }
 };
\ No newline at end of file
diff --git a/recovery.cpp b/recovery.cpp
index 233e562..6f62ff1 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -53,6 +53,7 @@
 #include <healthd/BatteryMonitor.h>
 #include <private/android_logger.h> /* private pmsg functions */
 #include <private/android_filesystem_config.h>  /* for AID_SYSTEM */
+#include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
 #include <ziparchive/zip_archive.h>
@@ -757,12 +758,13 @@
 }
 
 static bool prompt_and_wipe_data(Device* device) {
+  // Use a single string and let ScreenRecoveryUI handles the wrapping.
   const char* const headers[] = {
-    "Can't load Android system. Your data may be corrupt.",
-    "If you continue to get this message, you may need to",
-    "perform a factory data reset and erase all user data",
+    "Can't load Android system. Your data may be corrupt. "
+    "If you continue to get this message, you may need to "
+    "perform a factory data reset and erase all user data "
     "stored on this device.",
-    NULL
+    nullptr
   };
   const char* const items[] = {
     "Try again",
@@ -1479,12 +1481,8 @@
     ui->SetBackground(RecoveryUI::NONE);
     if (show_text) ui->ShowText(true);
 
-    struct selinux_opt seopts[] = {
-        { SELABEL_OPT_PATH, "/file_contexts" }
-    };
-
-    sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
-
+    sehandle = selinux_android_file_context_handle();
+    selinux_android_set_sehandle(sehandle);
     if (!sehandle) {
         ui->Print("Warning: No file_contexts\n");
     }
diff --git a/screen_ui.cpp b/screen_ui.cpp
index c83a765..5c93b66 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -278,6 +278,34 @@
   return offset;
 }
 
+int ScreenRecoveryUI::DrawWrappedTextLines(int x, int y, const char* const* lines) const {
+  int offset = 0;
+  for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) {
+    // The line will be wrapped if it exceeds text_cols_.
+    std::string line(lines[i]);
+    size_t next_start = 0;
+    while (next_start < line.size()) {
+      std::string sub = line.substr(next_start, text_cols_ + 1);
+      if (sub.size() <= text_cols_) {
+        next_start += sub.size();
+      } else {
+        // Line too long and must be wrapped to text_cols_ columns.
+        size_t last_space = sub.find_last_of(" \t\n");
+        if (last_space == std::string::npos) {
+          // No space found, just draw as much as we can
+          sub.resize(text_cols_);
+          next_start += text_cols_;
+        } else {
+          sub.resize(last_space);
+          next_start += last_space + 1;
+        }
+      }
+      offset += DrawTextLine(x, y + offset, sub.c_str(), false);
+    }
+  }
+  return offset;
+}
+
 static const char* REGULAR_HELP[] = {
   "Use volume up/down and power.",
   NULL
@@ -316,7 +344,8 @@
     y += DrawTextLines(x, y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP);
 
     SetColor(HEADER);
-    y += DrawTextLines(x, y, menu_headers_);
+    // Ignore kMenuIndent, which is not taken into account by text_cols_.
+    y += DrawWrappedTextLines(kMarginWidth, y, menu_headers_);
 
     SetColor(MENU);
     y += DrawHorizontalRule(y) + 4;
diff --git a/screen_ui.h b/screen_ui.h
index 1f40164..62dda75 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -187,6 +187,9 @@
   virtual int DrawTextLine(int x, int y, const char* line, bool bold) const;
   // Draws multiple text lines. Returns the offset it should be moving along Y-axis.
   int DrawTextLines(int x, int y, const char* const* lines) const;
+  // Similar to DrawTextLines() to draw multiple text lines, but additionally wraps long lines.
+  // Returns the offset it should be moving along Y-axis.
+  int DrawWrappedTextLines(int x, int y, const char* const* lines) const;
 };
 
 #endif  // RECOVERY_UI_H
diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp
index 5338f05..e520f50 100644
--- a/tests/component/verifier_test.cpp
+++ b/tests/component/verifier_test.cpp
@@ -33,6 +33,8 @@
 #include "otautil/SysUtil.h"
 #include "verifier.h"
 
+using namespace std::string_literals;
+
 class VerifierTest : public testing::TestWithParam<std::vector<std::string>> {
  protected:
   void SetUp() override {
@@ -115,6 +117,21 @@
   ASSERT_FALSE(load_keys(key_file5.path, certs));
 }
 
+TEST(VerifierTest, BadPackage_SignatureStartOutOfBounds) {
+  std::string testkey_v3;
+  ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3));
+
+  TemporaryFile key_file;
+  ASSERT_TRUE(android::base::WriteStringToFile(testkey_v3, key_file.path));
+  std::vector<Certificate> certs;
+  ASSERT_TRUE(load_keys(key_file.path, certs));
+
+  // Signature start is 65535 (0xffff) while comment size is 0 (Bug: 31914369).
+  std::string package = "\x50\x4b\x05\x06"s + std::string(12, '\0') + "\xff\xff\xff\xff\x00\x00"s;
+  ASSERT_EQ(VERIFY_FAILURE, verify_file(reinterpret_cast<const unsigned char*>(package.data()),
+                                        package.size(), certs));
+}
+
 TEST(VerifierTest, BadPackage_AlteredFooter) {
   std::string testkey_v3;
   ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3));
diff --git a/tests/unit/rangeset_test.cpp b/tests/unit/rangeset_test.cpp
index 3993cb9..15bcec8 100644
--- a/tests/unit/rangeset_test.cpp
+++ b/tests/unit/rangeset_test.cpp
@@ -21,7 +21,7 @@
 
 #include <gtest/gtest.h>
 
-#include "updater/rangeset.h"
+#include "rangeset.h"
 
 TEST(RangeSetTest, Parse_smoke) {
   RangeSet rs = RangeSet::Parse("2,1,10");
diff --git a/tools/recovery_l10n/res/values-az-rAZ/strings.xml b/tools/recovery_l10n/res/values-az/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-az-rAZ/strings.xml
rename to tools/recovery_l10n/res/values-az/strings.xml
diff --git a/tools/recovery_l10n/res/values-b+sr+Latn/strings.xml b/tools/recovery_l10n/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..c2d8f22
--- /dev/null
+++ b/tools/recovery_l10n/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="recovery_installing" msgid="2013591905463558223">"Ažuriranje sistema se instalira"</string>
+    <string name="recovery_erasing" msgid="7334826894904037088">"Briše se"</string>
+    <string name="recovery_no_command" msgid="4465476568623024327">"Nema komande"</string>
+    <string name="recovery_error" msgid="5748178989622716736">"Greška!"</string>
+    <string name="recovery_installing_security" msgid="9184031299717114342">"Instalira se bezbednosno ažuriranje"</string>
+</resources>
diff --git a/tools/recovery_l10n/res/values-be/strings.xml b/tools/recovery_l10n/res/values-be/strings.xml
new file mode 100644
index 0000000..7c0954d
--- /dev/null
+++ b/tools/recovery_l10n/res/values-be/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="recovery_installing" msgid="2013591905463558223">"Усталёўка абнаўлення сістэмы"</string>
+    <string name="recovery_erasing" msgid="7334826894904037088">"Сціранне"</string>
+    <string name="recovery_no_command" msgid="4465476568623024327">"Няма каманды"</string>
+    <string name="recovery_error" msgid="5748178989622716736">"Памылка"</string>
+    <string name="recovery_installing_security" msgid="9184031299717114342">"Усталёўка абнаўлення сістэмы бяспекі"</string>
+</resources>
diff --git a/tools/recovery_l10n/res/values-bn-rBD/strings.xml b/tools/recovery_l10n/res/values-bn/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-bn-rBD/strings.xml
rename to tools/recovery_l10n/res/values-bn/strings.xml
diff --git a/tools/recovery_l10n/res/values-bs/strings.xml b/tools/recovery_l10n/res/values-bs/strings.xml
new file mode 100644
index 0000000..412cf02
--- /dev/null
+++ b/tools/recovery_l10n/res/values-bs/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="recovery_installing" msgid="2013591905463558223">"Ažuriranje sistema…"</string>
+    <string name="recovery_erasing" msgid="7334826894904037088">"Brisanje u toku"</string>
+    <string name="recovery_no_command" msgid="4465476568623024327">"Nema komande"</string>
+    <string name="recovery_error" msgid="5748178989622716736">"Greška!"</string>
+    <string name="recovery_installing_security" msgid="9184031299717114342">"Instaliranje sigurnosnog ažuriranja…"</string>
+</resources>
diff --git a/tools/recovery_l10n/res/values-et-rEE/strings.xml b/tools/recovery_l10n/res/values-et/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-et-rEE/strings.xml
rename to tools/recovery_l10n/res/values-et/strings.xml
diff --git a/tools/recovery_l10n/res/values-eu-rES/strings.xml b/tools/recovery_l10n/res/values-eu/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-eu-rES/strings.xml
rename to tools/recovery_l10n/res/values-eu/strings.xml
diff --git a/tools/recovery_l10n/res/values-gl-rES/strings.xml b/tools/recovery_l10n/res/values-gl/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-gl-rES/strings.xml
rename to tools/recovery_l10n/res/values-gl/strings.xml
diff --git a/tools/recovery_l10n/res/values-gu-rIN/strings.xml b/tools/recovery_l10n/res/values-gu/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-gu-rIN/strings.xml
rename to tools/recovery_l10n/res/values-gu/strings.xml
diff --git a/tools/recovery_l10n/res/values-hy-rAM/strings.xml b/tools/recovery_l10n/res/values-hy/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-hy-rAM/strings.xml
rename to tools/recovery_l10n/res/values-hy/strings.xml
diff --git a/tools/recovery_l10n/res/values-is-rIS/strings.xml b/tools/recovery_l10n/res/values-is/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-is-rIS/strings.xml
rename to tools/recovery_l10n/res/values-is/strings.xml
diff --git a/tools/recovery_l10n/res/values-ka-rGE/strings.xml b/tools/recovery_l10n/res/values-ka/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-ka-rGE/strings.xml
rename to tools/recovery_l10n/res/values-ka/strings.xml
diff --git a/tools/recovery_l10n/res/values-kk-rKZ/strings.xml b/tools/recovery_l10n/res/values-kk/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-kk-rKZ/strings.xml
rename to tools/recovery_l10n/res/values-kk/strings.xml
diff --git a/tools/recovery_l10n/res/values-km-rKH/strings.xml b/tools/recovery_l10n/res/values-km/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-km-rKH/strings.xml
rename to tools/recovery_l10n/res/values-km/strings.xml
diff --git a/tools/recovery_l10n/res/values-kn-rIN/strings.xml b/tools/recovery_l10n/res/values-kn/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-kn-rIN/strings.xml
rename to tools/recovery_l10n/res/values-kn/strings.xml
diff --git a/tools/recovery_l10n/res/values-ky-rKG/strings.xml b/tools/recovery_l10n/res/values-ky/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-ky-rKG/strings.xml
rename to tools/recovery_l10n/res/values-ky/strings.xml
diff --git a/tools/recovery_l10n/res/values-lo-rLA/strings.xml b/tools/recovery_l10n/res/values-lo/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-lo-rLA/strings.xml
rename to tools/recovery_l10n/res/values-lo/strings.xml
diff --git a/tools/recovery_l10n/res/values-mk-rMK/strings.xml b/tools/recovery_l10n/res/values-mk/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-mk-rMK/strings.xml
rename to tools/recovery_l10n/res/values-mk/strings.xml
diff --git a/tools/recovery_l10n/res/values-ml-rIN/strings.xml b/tools/recovery_l10n/res/values-ml/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-ml-rIN/strings.xml
rename to tools/recovery_l10n/res/values-ml/strings.xml
diff --git a/tools/recovery_l10n/res/values-mn-rMN/strings.xml b/tools/recovery_l10n/res/values-mn/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-mn-rMN/strings.xml
rename to tools/recovery_l10n/res/values-mn/strings.xml
diff --git a/tools/recovery_l10n/res/values-mr-rIN/strings.xml b/tools/recovery_l10n/res/values-mr/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-mr-rIN/strings.xml
rename to tools/recovery_l10n/res/values-mr/strings.xml
diff --git a/tools/recovery_l10n/res/values-ms-rMY/strings.xml b/tools/recovery_l10n/res/values-ms/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-ms-rMY/strings.xml
rename to tools/recovery_l10n/res/values-ms/strings.xml
diff --git a/tools/recovery_l10n/res/values-my-rMM/strings.xml b/tools/recovery_l10n/res/values-my/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-my-rMM/strings.xml
rename to tools/recovery_l10n/res/values-my/strings.xml
diff --git a/tools/recovery_l10n/res/values-ne-rNP/strings.xml b/tools/recovery_l10n/res/values-ne/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-ne-rNP/strings.xml
rename to tools/recovery_l10n/res/values-ne/strings.xml
diff --git a/tools/recovery_l10n/res/values-pa-rIN/strings.xml b/tools/recovery_l10n/res/values-pa/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-pa-rIN/strings.xml
rename to tools/recovery_l10n/res/values-pa/strings.xml
diff --git a/tools/recovery_l10n/res/values-si-rLK/strings.xml b/tools/recovery_l10n/res/values-si/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-si-rLK/strings.xml
rename to tools/recovery_l10n/res/values-si/strings.xml
diff --git a/tools/recovery_l10n/res/values-sq-rAL/strings.xml b/tools/recovery_l10n/res/values-sq/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-sq-rAL/strings.xml
rename to tools/recovery_l10n/res/values-sq/strings.xml
diff --git a/tools/recovery_l10n/res/values-ta-rIN/strings.xml b/tools/recovery_l10n/res/values-ta/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-ta-rIN/strings.xml
rename to tools/recovery_l10n/res/values-ta/strings.xml
diff --git a/tools/recovery_l10n/res/values-te-rIN/strings.xml b/tools/recovery_l10n/res/values-te/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-te-rIN/strings.xml
rename to tools/recovery_l10n/res/values-te/strings.xml
diff --git a/tools/recovery_l10n/res/values-ur-rPK/strings.xml b/tools/recovery_l10n/res/values-ur/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-ur-rPK/strings.xml
rename to tools/recovery_l10n/res/values-ur/strings.xml
diff --git a/tools/recovery_l10n/res/values-uz-rUZ/strings.xml b/tools/recovery_l10n/res/values-uz/strings.xml
similarity index 100%
rename from tools/recovery_l10n/res/values-uz-rUZ/strings.xml
rename to tools/recovery_l10n/res/values-uz/strings.xml
diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp
index 35998bc..7a2ccbc 100644
--- a/uncrypt/uncrypt.cpp
+++ b/uncrypt/uncrypt.cpp
@@ -625,12 +625,12 @@
     }
 
     if (action == UNCRYPT_DEBUG) {
-        LOG(INFO) << "uncrypt called in debug mode, skip socket communication\n";
+        LOG(INFO) << "uncrypt called in debug mode, skip socket communication";
         bool success = uncrypt_wrapper(input_path, map_file, -1);
         if (success) {
-            LOG(INFO) << "uncrypt succeeded\n";
+            LOG(INFO) << "uncrypt succeeded";
         } else{
-            LOG(INFO) << "uncrypt failed\n";
+            LOG(INFO) << "uncrypt failed";
         }
         return success ? 0 : 1;
     }
diff --git a/update_verifier/update_verifier_main.cpp b/update_verifier/update_verifier_main.cpp
index 46e8bbb..9dd5a0c 100644
--- a/update_verifier/update_verifier_main.cpp
+++ b/update_verifier/update_verifier_main.cpp
@@ -16,8 +16,14 @@
 
 // See the comments in update_verifier.cpp.
 
+#include <android-base/logging.h>
+
 #include "update_verifier/update_verifier.h"
 
 int main(int argc, char** argv) {
+  // Set up update_verifier logging to be written to kmsg; because we may not have Logd during
+  // boot time.
+  android::base::InitLogging(argv, &android::base::KernelLogger);
+
   return update_verifier(argc, argv);
 }
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index a0b9ad2..fe21dd0 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -53,8 +53,8 @@
 #include "error_code.h"
 #include "ota_io.h"
 #include "print_sha1.h"
+#include "rangeset.h"
 #include "updater/install.h"
-#include "updater/rangeset.h"
 #include "updater/updater.h"
 
 // Set this to 0 to interpret 'erase' transfers to mean do a
diff --git a/updater/updater.cpp b/updater/updater.cpp
index f5ff6df..1d8fa8e 100644
--- a/updater/updater.cpp
+++ b/updater/updater.cpp
@@ -25,6 +25,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/strings.h>
+#include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
 #include <ziparchive/zip_archive.h>
@@ -139,9 +140,8 @@
     return 6;
   }
 
-  struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, "/file_contexts" } };
-
-  sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
+  sehandle = selinux_android_file_context_handle();
+  selinux_android_set_sehandle(sehandle);
 
   if (!sehandle) {
     fprintf(cmd_pipe, "ui_print Warning: No file_contexts\n");