Merge "Add libbrotli as a dependency when building applypatch binary"
diff --git a/Android.bp b/Android.bp
index 22407e0..f8c6a4b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -4,4 +4,5 @@
     "edify",
     "otafault",
     "otautil",
+    "uncrypt",
 ]
diff --git a/Android.mk b/Android.mk
index 7e34c10..d9966b7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -158,7 +158,6 @@
     libziparchive \
     libotautil \
     libmounts \
-    libz \
     libminadbd \
     libasyncio \
     libfusesideload \
@@ -173,7 +172,8 @@
     libcutils \
     libutils \
     liblog \
-    libselinux
+    libselinux \
+    libz
 
 LOCAL_HAL_STATIC_LIBRARIES := libhealthd
 
@@ -263,6 +263,5 @@
     $(LOCAL_PATH)/minui/Android.mk \
     $(LOCAL_PATH)/tests/Android.mk \
     $(LOCAL_PATH)/tools/Android.mk \
-    $(LOCAL_PATH)/uncrypt/Android.mk \
     $(LOCAL_PATH)/updater/Android.mk \
     $(LOCAL_PATH)/update_verifier/Android.mk \
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 0000000..b5f5f03
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,6 @@
+[Builtin Hooks]
+clang_format = true
+
+[Builtin Hooks Options]
+# Handle native codes only.
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
index c8b75df..41a72eb 100644
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -42,6 +42,8 @@
 #include "otafault/ota_io.h"
 #include "otautil/print_sha1.h"
 
+std::string cache_temp_source = "/cache/saved.file";
+
 static int LoadPartitionContents(const std::string& filename, FileContents* file);
 static size_t FileSink(const unsigned char* data, size_t len, int fd);
 static int GenerateTarget(const FileContents& source_file, const std::unique_ptr<Value>& patch,
@@ -411,12 +413,10 @@
       (!patch_sha1_str.empty() && FindMatchingPatch(file.sha1, patch_sha1_str) < 0)) {
     printf("file \"%s\" doesn't have any of expected sha1 sums; checking cache\n", filename);
 
-    // If the source file is missing or corrupted, it might be because
-    // we were killed in the middle of patching it.  A copy of it
-    // should have been made in CACHE_TEMP_SOURCE.  If that file
-    // exists and matches the sha1 we're looking for, the check still
-    // passes.
-    if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) {
+    // If the source file is missing or corrupted, it might be because we were killed in the middle
+    // of patching it.  A copy of it should have been made in cache_temp_source.  If that file
+    // exists and matches the sha1 we're looking for, the check still passes.
+    if (LoadFileContents(cache_temp_source.c_str(), &file) != 0) {
       printf("failed to load cache file\n");
       return 1;
     }
@@ -539,7 +539,7 @@
   printf("source file is bad; trying copy\n");
 
   FileContents copy_file;
-  if (LoadFileContents(CACHE_TEMP_SOURCE, &copy_file) < 0) {
+  if (LoadFileContents(cache_temp_source.c_str(), &copy_file) < 0) {
     printf("failed to read copy file\n");
     return 1;
   }
@@ -634,7 +634,7 @@
     printf("not enough free space on /cache\n");
     return 1;
   }
-  if (SaveFileContents(CACHE_TEMP_SOURCE, &source_file) < 0) {
+  if (SaveFileContents(cache_temp_source.c_str(), &source_file) < 0) {
     printf("failed to back up source file\n");
     return 1;
   }
@@ -651,11 +651,11 @@
 
   int result;
   if (use_bsdiff) {
-    result = ApplyBSDiffPatch(source_file.data.data(), source_file.data.size(), patch.get(), 0,
-                              sink, &ctx);
+    result =
+        ApplyBSDiffPatch(source_file.data.data(), source_file.data.size(), *patch, 0, sink, &ctx);
   } else {
-    result = ApplyImagePatch(source_file.data.data(), source_file.data.size(), patch.get(), sink,
-                             &ctx, bonus_data);
+    result = ApplyImagePatch(source_file.data.data(), source_file.data.size(), *patch, sink, &ctx,
+                             bonus_data);
   }
 
   if (result != 0) {
@@ -680,7 +680,7 @@
   }
 
   // Delete the backup copy of the source.
-  unlink(CACHE_TEMP_SOURCE);
+  unlink(cache_temp_source.c_str());
 
   // Success!
   return 0;
diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp
index c291464..912dbbd 100644
--- a/applypatch/bspatch.cpp
+++ b/applypatch/bspatch.cpp
@@ -26,7 +26,7 @@
 #include <string>
 
 #include <android-base/logging.h>
-#include <bspatch.h>
+#include <bsdiff/bspatch.h>
 #include <openssl/sha.h>
 
 #include "applypatch/applypatch.h"
@@ -65,7 +65,7 @@
         );
 }
 
-int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch,
+int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value& patch,
                      size_t patch_offset, SinkFn sink, SHA_CTX* ctx) {
   auto sha_sink = [&sink, &ctx](const uint8_t* data, size_t len) {
     len = sink(data, len);
@@ -73,22 +73,20 @@
     return len;
   };
 
-  CHECK(patch != nullptr);
-  CHECK_LE(patch_offset, patch->data.size());
+  CHECK_LE(patch_offset, patch.data.size());
 
   int result = bsdiff::bspatch(old_data, old_size,
-                               reinterpret_cast<const uint8_t*>(&patch->data[patch_offset]),
-                               patch->data.size() - patch_offset, sha_sink);
+                               reinterpret_cast<const uint8_t*>(&patch.data[patch_offset]),
+                               patch.data.size() - patch_offset, sha_sink);
   if (result != 0) {
     LOG(ERROR) << "bspatch failed, result: " << result;
     // print SHA1 of the patch in the case of a data error.
     if (result == 2) {
       uint8_t digest[SHA_DIGEST_LENGTH];
-      SHA1(reinterpret_cast<const uint8_t*>(patch->data.data() + patch_offset),
-           patch->data.size() - patch_offset, digest);
+      SHA1(reinterpret_cast<const uint8_t*>(patch.data.data() + patch_offset),
+           patch.data.size() - patch_offset, digest);
       std::string patch_sha1 = print_sha1(digest);
-      LOG(ERROR) << "Patch may be corrupted, offset: " << patch_offset << ", SHA1: "
-                 << patch_sha1;
+      LOG(ERROR) << "Patch may be corrupted, offset: " << patch_offset << ", SHA1: " << patch_sha1;
     }
   }
   return result;
diff --git a/applypatch/freecache.cpp b/applypatch/freecache.cpp
index 331cae2..0a40baa 100644
--- a/applypatch/freecache.cpp
+++ b/applypatch/freecache.cpp
@@ -90,10 +90,9 @@
     while ((de = readdir(d.get())) != 0) {
       std::string path = std::string(dirs[i]) + "/" + de->d_name;
 
-      // We can't delete CACHE_TEMP_SOURCE; if it's there we might have
-      // restarted during installation and could be depending on it to
-      // be there.
-      if (path == CACHE_TEMP_SOURCE) {
+      // We can't delete cache_temp_source; if it's there we might have restarted during
+      // installation and could be depending on it to be there.
+      if (path == cache_temp_source) {
         continue;
       }
 
diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp
index f57e794..3dae63d 100644
--- a/applypatch/imgdiff.cpp
+++ b/applypatch/imgdiff.cpp
@@ -175,7 +175,7 @@
 static constexpr size_t VERSION = 2;
 
 // We assume the header "IMGDIFF#" is 8 bytes.
-static_assert(VERSION <= 9, "VERSION occupies more than one byte.");
+static_assert(VERSION <= 9, "VERSION occupies more than one byte");
 
 static constexpr size_t BLOCK_SIZE = 4096;
 static constexpr size_t BUFFER_SIZE = 0x8000;
@@ -229,8 +229,8 @@
   }
 
   // TODO find the largest non-overlap chunk.
-  printf("Removing block %s from %zu - %zu\n", used_ranges.ToString().c_str(), *start,
-         *start + *length - 1);
+  LOG(INFO) << "Removing block " << used_ranges.ToString() << " from " << *start << " - "
+            << *start + *length - 1;
 
   // If there's no duplicate entry name, we should only overlap in the head or tail block. Try to
   // trim both blocks. Skip this source chunk in case it still overlaps with the used ranges.
@@ -241,7 +241,7 @@
     return true;
   }
 
-  printf("Failed to remove the overlapped block ranges; skip the source\n");
+  LOG(WARNING) << "Failed to remove the overlapped block ranges; skip the source";
   return false;
 }
 
@@ -251,6 +251,7 @@
   { "block-limit", required_argument, nullptr, 0 },
   { "debug-dir", required_argument, nullptr, 0 },
   { "split-info", required_argument, nullptr, 0 },
+  { "verbose", no_argument, nullptr, 'v' },
   { nullptr, 0, nullptr, 0 },
 };
 
@@ -284,6 +285,11 @@
   return raw_data_len_;
 }
 
+void ImageChunk::Dump(size_t index) const {
+  LOG(INFO) << "chunk: " << index << ", type: " << type_ << ", start: " << start_
+            << ", len: " << DataLengthForPatch() << ", name: " << entry_name_;
+}
+
 bool ImageChunk::operator==(const ImageChunk& other) const {
   if (type_ != other.type_) {
     return false;
@@ -334,7 +340,7 @@
 
   int fd = mkstemp(ptemp);
   if (fd == -1) {
-    printf("MakePatch failed to create a temporary file: %s\n", strerror(errno));
+    PLOG(ERROR) << "MakePatch failed to create a temporary file";
     return false;
   }
   close(fd);
@@ -342,18 +348,18 @@
   int r = bsdiff::bsdiff(src.DataForPatch(), src.DataLengthForPatch(), tgt.DataForPatch(),
                          tgt.DataLengthForPatch(), ptemp, bsdiff_cache);
   if (r != 0) {
-    printf("bsdiff() failed: %d\n", r);
+    LOG(ERROR) << "bsdiff() failed: " << r;
     return false;
   }
 
   android::base::unique_fd patch_fd(open(ptemp, O_RDONLY));
   if (patch_fd == -1) {
-    printf("failed to open %s: %s\n", ptemp, strerror(errno));
+    PLOG(ERROR) << "Failed to open " << ptemp;
     return false;
   }
   struct stat st;
   if (fstat(patch_fd, &st) != 0) {
-    printf("failed to stat patch file %s: %s\n", ptemp, strerror(errno));
+    PLOG(ERROR) << "Failed to stat patch file " << ptemp;
     return false;
   }
 
@@ -361,7 +367,7 @@
 
   patch_data->resize(sz);
   if (!android::base::ReadFully(patch_fd, patch_data->data(), sz)) {
-    printf("failed to read \"%s\" %s\n", ptemp, strerror(errno));
+    PLOG(ERROR) << "Failed to read " << ptemp;
     unlink(ptemp);
     return false;
   }
@@ -373,7 +379,7 @@
 
 bool ImageChunk::ReconstructDeflateChunk() {
   if (type_ != CHUNK_DEFLATE) {
-    printf("attempt to reconstruct non-deflate chunk\n");
+    LOG(ERROR) << "Attempted to reconstruct non-deflate chunk";
     return false;
   }
 
@@ -403,7 +409,7 @@
   strm.next_in = uncompressed_data_.data();
   int ret = deflateInit2(&strm, level, METHOD, WINDOWBITS, MEMLEVEL, STRATEGY);
   if (ret < 0) {
-    printf("failed to initialize deflate: %d\n", ret);
+    LOG(ERROR) << "Failed to initialize deflate: " << ret;
     return false;
   }
 
@@ -414,7 +420,7 @@
     strm.next_out = buffer.data();
     ret = deflate(&strm, Z_FINISH);
     if (ret < 0) {
-      printf("failed to deflate: %d\n", ret);
+      LOG(ERROR) << "Failed to deflate: " << ret;
       return false;
     }
 
@@ -490,17 +496,19 @@
 }
 
 // Return the offset of the next patch into the patch data.
-size_t PatchChunk::WriteHeaderToFd(int fd, size_t offset) const {
+size_t PatchChunk::WriteHeaderToFd(int fd, size_t offset, size_t index) const {
   Write4(fd, type_);
   switch (type_) {
     case CHUNK_NORMAL:
-      printf("normal   (%10zu, %10zu)  %10zu\n", target_start_, target_len_, data_.size());
+      LOG(INFO) << android::base::StringPrintf("chunk %zu: normal   (%10zu, %10zu)  %10zu", index,
+                                               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());
+      LOG(INFO) << android::base::StringPrintf("chunk %zu: deflate  (%10zu, %10zu)  %10zu", index,
+                                               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));
@@ -513,10 +521,11 @@
       Write4(fd, ImageChunk::STRATEGY);
       return offset + data_.size();
     case CHUNK_RAW:
-      printf("raw      (%10zu, %10zu)\n", target_start_, target_len_);
+      LOG(INFO) << android::base::StringPrintf("chunk %zu: raw      (%10zu, %10zu)", index,
+                                               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";
+        CHECK(false) << "Failed to write " << data_.size() << " bytes patch";
       }
       return offset;
     default:
@@ -545,14 +554,14 @@
 
   // Write out the headers.
   if (!android::base::WriteStringToFd("IMGDIFF" + std::to_string(VERSION), patch_fd)) {
-    printf("failed to write \"IMGDIFF%zu\": %s\n", VERSION, strerror(errno));
+    PLOG(ERROR) << "Failed to write \"IMGDIFF" << VERSION << "\"";
     return false;
   }
 
   Write4(patch_fd, static_cast<int32_t>(patch_chunks.size()));
+  LOG(INFO) << "Writing " << patch_chunks.size() << " patch headers...";
   for (size_t i = 0; i < patch_chunks.size(); ++i) {
-    printf("chunk %zu: ", i);
-    offset = patch_chunks[i].WriteHeaderToFd(patch_fd, offset);
+    offset = patch_chunks[i].WriteHeaderToFd(patch_fd, offset, i);
   }
 
   // Append each chunk's bsdiff patch, in order.
@@ -561,7 +570,7 @@
       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());
+      PLOG(ERROR) << "Failed to write " << patch.data_.size() << " bytes patch to patch_fd";
       return false;
     }
   }
@@ -603,10 +612,9 @@
 
 void Image::DumpChunks() const {
   std::string type = is_source_ ? "source" : "target";
-  printf("Dumping chunks for %s\n", type.c_str());
+  LOG(INFO) << "Dumping chunks for " << type;
   for (size_t i = 0; i < chunks_.size(); ++i) {
-    printf("chunk %zu: ", i);
-    chunks_[i].Dump();
+    chunks_[i].Dump(i);
   }
 }
 
@@ -615,19 +623,19 @@
 
   android::base::unique_fd fd(open(filename.c_str(), O_RDONLY));
   if (fd == -1) {
-    printf("failed to open \"%s\" %s\n", filename.c_str(), strerror(errno));
+    PLOG(ERROR) << "Failed to open " << filename;
     return false;
   }
   struct stat st;
   if (fstat(fd, &st) != 0) {
-    printf("failed to stat \"%s\": %s\n", filename.c_str(), strerror(errno));
+    PLOG(ERROR) << "Failed to stat " << filename;
     return false;
   }
 
   size_t sz = static_cast<size_t>(st.st_size);
   file_content->resize(sz);
   if (!android::base::ReadFully(fd, file_content->data(), sz)) {
-    printf("failed to read \"%s\" %s\n", filename.c_str(), strerror(errno));
+    PLOG(ERROR) << "Failed to read " << filename;
     return false;
   }
   fd.reset();
@@ -643,14 +651,14 @@
   // Omit the trailing zeros before we pass the file to ziparchive handler.
   size_t zipfile_size;
   if (!GetZipFileSize(&zipfile_size)) {
-    printf("failed to parse the actual size of %s\n", filename.c_str());
+    LOG(ERROR) << "Failed to parse the actual size of " << filename;
     return false;
   }
   ZipArchiveHandle handle;
   int err = OpenArchiveFromMemory(const_cast<uint8_t*>(file_content_.data()), zipfile_size,
                                   filename.c_str(), &handle);
   if (err != 0) {
-    printf("failed to open zip file %s: %s\n", filename.c_str(), ErrorCodeString(err));
+    LOG(ERROR) << "Failed to open zip file " << filename << ": " << ErrorCodeString(err);
     CloseArchive(handle);
     return false;
   }
@@ -669,7 +677,7 @@
   void* cookie;
   int ret = StartIteration(handle, &cookie, nullptr, nullptr);
   if (ret != 0) {
-    printf("failed to iterate over entries in %s: %s\n", filename.c_str(), ErrorCodeString(ret));
+    LOG(ERROR) << "Failed to iterate over entries in " << filename << ": " << ErrorCodeString(ret);
     return false;
   }
 
@@ -685,7 +693,7 @@
   }
 
   if (ret != -1) {
-    printf("Error while iterating over zip entries: %s\n", ErrorCodeString(ret));
+    LOG(ERROR) << "Error while iterating over zip entries: " << ErrorCodeString(ret);
     return false;
   }
   std::sort(temp_entries.begin(), temp_entries.end(),
@@ -697,7 +705,7 @@
   if (is_source_) {
     for (auto& entry : temp_entries) {
       if (!AddZipEntryToChunks(handle, entry.first, &entry.second)) {
-        printf("Failed to add %s to source chunks\n", entry.first.c_str());
+        LOG(ERROR) << "Failed to add " << entry.first << " to source chunks";
         return false;
       }
     }
@@ -725,7 +733,7 @@
       // Add the next zip entry.
       std::string entry_name = temp_entries[nextentry].first;
       if (!AddZipEntryToChunks(handle, entry_name, &temp_entries[nextentry].second)) {
-        printf("Failed to add %s to target chunks\n", entry_name.c_str());
+        LOG(ERROR) << "Failed to add " << entry_name << " to target chunks";
         return false;
       }
 
@@ -771,8 +779,8 @@
     std::vector<uint8_t> uncompressed_data(uncompressed_len);
     int ret = ExtractToMemory(handle, entry, uncompressed_data.data(), uncompressed_len);
     if (ret != 0) {
-      printf("failed to extract %s with size %zu: %s\n", entry_name.c_str(), uncompressed_len,
-             ErrorCodeString(ret));
+      LOG(ERROR) << "Failed to extract " << entry_name << " with size " << uncompressed_len << ": "
+                 << ErrorCodeString(ret);
       return false;
     }
     ImageChunk curr(CHUNK_DEFLATE, entry->offset, &file_content_, compressed_len, entry_name);
@@ -793,7 +801,7 @@
 // offset 22: comment, n bytes
 bool ZipModeImage::GetZipFileSize(size_t* input_file_size) {
   if (file_content_.size() < 22) {
-    printf("file is too small to be a zip file\n");
+    LOG(ERROR) << "File is too small to be a zip file";
     return false;
   }
 
@@ -872,8 +880,8 @@
     } else if (!tgt_chunk.ReconstructDeflateChunk()) {
       // We cannot recompress the data and get exactly the same bits as are in the input target
       // image. Treat the chunk as a normal non-deflated chunk.
-      printf("failed to reconstruct target deflate chunk [%s]; treating as normal\n",
-             tgt_chunk.GetEntryName().c_str());
+      LOG(WARNING) << "Failed to reconstruct target deflate chunk [" << tgt_chunk.GetEntryName()
+                   << "]; treating as normal";
 
       tgt_chunk.ChangeDeflateChunkToNormal();
       src_chunk->ChangeDeflateChunkToNormal();
@@ -902,7 +910,7 @@
   size_t limit = tgt_image.limit_;
 
   src_image.DumpChunks();
-  printf("Splitting %zu tgt chunks...\n", tgt_image.NumOfChunks());
+  LOG(INFO) << "Splitting " << tgt_image.NumOfChunks() << " tgt chunks...";
 
   SortedRangeSet used_src_ranges;  // ranges used for previous split source images.
 
@@ -1049,7 +1057,7 @@
                                        size_t total_tgt_size) {
   CHECK_EQ(split_tgt_images.size(), split_src_images.size());
 
-  printf("Validating %zu images\n", split_tgt_images.size());
+  LOG(INFO) << "Validating " << split_tgt_images.size() << " images";
 
   // Verify that the target image pieces is continuous and can add up to the total size.
   size_t last_offset = 0;
@@ -1081,7 +1089,7 @@
 bool ZipModeImage::GeneratePatchesInternal(const ZipModeImage& tgt_image,
                                            const ZipModeImage& src_image,
                                            std::vector<PatchChunk>* patch_chunks) {
-  printf("Construct patches for %zu chunks...\n", tgt_image.NumOfChunks());
+  LOG(INFO) << "Constructing patches for " << tgt_image.NumOfChunks() << " chunks...";
   patch_chunks->clear();
 
   bsdiff::SuffixArrayIndexInterface* bsdiff_cache = nullptr;
@@ -1103,12 +1111,12 @@
 
     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());
+      LOG(ERROR) << "Failed to generate patch, name: " << tgt_chunk.GetEntryName();
       return false;
     }
 
-    printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data.size(),
-           tgt_chunk.GetRawDataLength());
+    LOG(INFO) << "patch " << i << " is " << patch_data.size() << " bytes (of "
+              << tgt_chunk.GetRawDataLength() << ")";
 
     if (PatchChunk::RawDataIsSmaller(tgt_chunk, patch_data.size())) {
       patch_chunks->emplace_back(tgt_chunk);
@@ -1133,7 +1141,7 @@
   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) {
-    printf("failed to open \"%s\": %s\n", patch_name.c_str(), strerror(errno));
+    PLOG(ERROR) << "Failed to open " << patch_name;
     return false;
   }
 
@@ -1146,12 +1154,12 @@
                                    const std::string& patch_name,
                                    const std::string& split_info_file,
                                    const std::string& debug_dir) {
-  printf("Construct patches for %zu split images...\n", split_tgt_images.size());
+  LOG(INFO) << "Constructing patches for " << split_tgt_images.size() << " split images...";
 
   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) {
-    printf("failed to open \"%s\": %s\n", patch_name.c_str(), strerror(errno));
+    PLOG(ERROR) << "Failed to open " << patch_name;
     return false;
   }
 
@@ -1160,7 +1168,7 @@
     std::vector<PatchChunk> patch_chunks;
     if (!ZipModeImage::GeneratePatchesInternal(split_tgt_images[i], split_src_images[i],
                                                &patch_chunks)) {
-      printf("failed to generate split patch\n");
+      LOG(ERROR) << "Failed to generate split patch";
       return false;
     }
 
@@ -1188,12 +1196,12 @@
           open(src_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR));
 
       if (fd == -1) {
-        printf("Failed to open %s\n", src_name.c_str());
+        PLOG(ERROR) << "Failed to open " << src_name;
         return false;
       }
       if (!android::base::WriteFully(fd, split_src_images[i].PseudoSource().DataForPatch(),
                                      split_src_images[i].PseudoSource().DataLengthForPatch())) {
-        printf("Failed to write split source data into %s\n", src_name.c_str());
+        PLOG(ERROR) << "Failed to write split source data into " << src_name;
         return false;
       }
 
@@ -1201,7 +1209,7 @@
       fd.reset(open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR));
 
       if (fd == -1) {
-        printf("Failed to open %s\n", patch_name.c_str());
+        PLOG(ERROR) << "Failed to open " << patch_name;
         return false;
       }
       if (!PatchChunk::WritePatchDataToFd(patch_chunks, fd)) {
@@ -1219,8 +1227,7 @@
   std::string split_info_string = android::base::StringPrintf(
       "%zu\n%zu\n", VERSION, split_info_list.size()) + android::base::Join(split_info_list, '\n');
   if (!android::base::WriteStringToFile(split_info_string, split_info_file)) {
-    printf("failed to write split info to \"%s\": %s\n", split_info_file.c_str(),
-           strerror(errno));
+    PLOG(ERROR) << "Failed to write split info to " << split_info_file;
     return false;
   }
 
@@ -1265,7 +1272,7 @@
       // not expect zlib headers.
       int ret = inflateInit2(&strm, -15);
       if (ret < 0) {
-        printf("failed to initialize inflate: %d\n", ret);
+        LOG(ERROR) << "Failed to initialize inflate: " << ret;
         return false;
       }
 
@@ -1277,8 +1284,8 @@
         strm.next_out = uncompressed_data.data() + uncompressed_len;
         ret = inflate(&strm, Z_NO_FLUSH);
         if (ret < 0) {
-          printf("Warning: inflate failed [%s] at offset [%zu], treating as a normal chunk\n",
-                 strm.msg, chunk_offset);
+          LOG(WARNING) << "Inflate failed [" << strm.msg << "] at offset [" << chunk_offset
+                       << "]; treating as a normal chunk";
           break;
         }
         uncompressed_len = allocated - strm.avail_out;
@@ -1299,13 +1306,13 @@
       // matches the size of the data we got when we actually did the decompression.
       size_t footer_index = pos + raw_data_len + GZIP_FOOTER_LEN - 4;
       if (sz - footer_index < 4) {
-        printf("Warning: invalid footer position; treating as a nomal chunk\n");
+        LOG(WARNING) << "invalid footer position; treating as a normal chunk";
         continue;
       }
       size_t footer_size = get_unaligned<uint32_t>(file_content_.data() + footer_index);
       if (footer_size != uncompressed_len) {
-        printf("Warning: footer size %zu != decompressed size %zu; treating as a nomal chunk\n",
-               footer_size, uncompressed_len);
+        LOG(WARNING) << "footer size " << footer_size << " != " << uncompressed_len
+                     << "; treating as a normal chunk";
         continue;
       }
 
@@ -1345,12 +1352,12 @@
 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");
+    LOG(ERROR) << "Failed to set bonus data";
     DumpChunks();
     return false;
   }
 
-  printf("  using %zu bytes of bonus data\n", bonus_data.size());
+  LOG(INFO) << "  using " << bonus_data.size() << " bytes of bonus data";
   return true;
 }
 
@@ -1362,14 +1369,14 @@
   src_image->MergeAdjacentNormalChunks();
 
   if (tgt_image->NumOfChunks() != src_image->NumOfChunks()) {
-    printf("source and target don't have same number of chunks!\n");
+    LOG(ERROR) << "Source and target don't have same number of chunks!";
     tgt_image->DumpChunks();
     src_image->DumpChunks();
     return false;
   }
   for (size_t i = 0; i < tgt_image->NumOfChunks(); ++i) {
     if ((*tgt_image)[i].GetType() != (*src_image)[i].GetType()) {
-      printf("source and target don't have same chunk structure! (chunk %zu)\n", i);
+      LOG(ERROR) << "Source and target don't have same chunk structure! (chunk " << i << ")";
       tgt_image->DumpChunks();
       src_image->DumpChunks();
       return false;
@@ -1390,8 +1397,8 @@
     } 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());
+      LOG(WARNING) << "Failed to reconstruct target deflate chunk " << i << " ["
+                   << tgt_chunk.GetEntryName() << "]; treating as normal";
       tgt_chunk.ChangeDeflateChunkToNormal();
       src_chunk.ChangeDeflateChunkToNormal();
     }
@@ -1403,7 +1410,7 @@
   src_image->MergeAdjacentNormalChunks();
   if (tgt_image->NumOfChunks() != src_image->NumOfChunks()) {
     // This shouldn't happen.
-    printf("merging normal chunks went awry\n");
+    LOG(ERROR) << "Merging normal chunks went awry";
     return false;
   }
 
@@ -1415,7 +1422,7 @@
 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());
+  LOG(INFO) << "Constructing patches for " << tgt_image.NumOfChunks() << " chunks...";
   std::vector<PatchChunk> patch_chunks;
   patch_chunks.reserve(tgt_image.NumOfChunks());
 
@@ -1430,11 +1437,11 @@
 
     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);
+      LOG(ERROR) << "Failed to generate patch for target chunk " << i;
       return false;
     }
-    printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data.size(),
-           tgt_chunk.GetRawDataLength());
+    LOG(INFO) << "patch " << i << " is " << patch_data.size() << " bytes (of "
+              << tgt_chunk.GetRawDataLength() << ")";
 
     if (PatchChunk::RawDataIsSmaller(tgt_chunk, patch_data.size())) {
       patch_chunks.emplace_back(tgt_chunk);
@@ -1448,7 +1455,7 @@
   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) {
-    printf("failed to open \"%s\": %s\n", patch_name.c_str(), strerror(errno));
+    PLOG(ERROR) << "Failed to open " << patch_name;
     return false;
   }
 
@@ -1456,6 +1463,7 @@
 }
 
 int imgdiff(int argc, const char** argv) {
+  bool verbose = false;
   bool zip_mode = false;
   std::vector<uint8_t> bonus_data;
   size_t blocks_limit = 0;
@@ -1464,9 +1472,10 @@
 
   int opt;
   int option_index;
-  optind = 1;  // Reset the getopt state so that we can call it multiple times for test.
+  optind = 0;  // Reset the getopt state so that we can call it multiple times for test.
 
-  while ((opt = getopt_long(argc, const_cast<char**>(argv), "zb:", OPTIONS, &option_index)) != -1) {
+  while ((opt = getopt_long(argc, const_cast<char**>(argv), "zb:v", OPTIONS, &option_index)) !=
+         -1) {
     switch (opt) {
       case 'z':
         zip_mode = true;
@@ -1474,27 +1483,30 @@
       case 'b': {
         android::base::unique_fd fd(open(optarg, O_RDONLY));
         if (fd == -1) {
-          printf("failed to open bonus file %s: %s\n", optarg, strerror(errno));
+          PLOG(ERROR) << "Failed to open bonus file " << optarg;
           return 1;
         }
         struct stat st;
         if (fstat(fd, &st) != 0) {
-          printf("failed to stat bonus file %s: %s\n", optarg, strerror(errno));
+          PLOG(ERROR) << "Failed to stat bonus file " << optarg;
           return 1;
         }
 
         size_t bonus_size = st.st_size;
         bonus_data.resize(bonus_size);
         if (!android::base::ReadFully(fd, bonus_data.data(), bonus_size)) {
-          printf("failed to read bonus file %s: %s\n", optarg, strerror(errno));
+          PLOG(ERROR) << "Failed to read bonus file " << optarg;
           return 1;
         }
         break;
       }
+      case 'v':
+        verbose = true;
+        break;
       case 0: {
         std::string name = OPTIONS[option_index].name;
         if (name == "block-limit" && !android::base::ParseUint(optarg, &blocks_limit)) {
-          printf("failed to parse size blocks_limit: %s\n", optarg);
+          LOG(ERROR) << "Failed to parse size blocks_limit: " << optarg;
           return 1;
         } else if (name == "split-info") {
           split_info_file = optarg;
@@ -1504,22 +1516,28 @@
         break;
       }
       default:
-        printf("unexpected opt: %s\n", optarg);
+        LOG(ERROR) << "unexpected opt: " << static_cast<char>(opt);
         return 2;
     }
   }
 
+  if (!verbose) {
+    android::base::SetMinimumLogSeverity(android::base::WARNING);
+  }
+
   if (argc - optind != 3) {
-    printf("usage: %s [options] <src-img> <tgt-img> <patch-file>\n", argv[0]);
-    printf(
-        "  -z <zip-mode>,    Generate patches in zip mode, src and tgt should be zip files.\n"
-        "  -b <bonus-file>,  Bonus file in addition to src, image mode only.\n"
-        "  --block-limit,    For large zips, split the src and tgt based on the block limit;\n"
-        "                    and generate patches between each pair of pieces. Concatenate these\n"
-        "                    patches together and output them into <patch-file>.\n"
-        "  --split-info,     Output the split information (patch_size, tgt_size, src_ranges);\n"
-        "                    zip mode with block-limit only.\n"
-        "  --debug_dir,      Debug directory to put the split srcs and patches, zip mode only.\n");
+    LOG(ERROR) << "usage: " << argv[0] << " [options] <src-img> <tgt-img> <patch-file>";
+    LOG(ERROR)
+        << "  -z <zip-mode>,    Generate patches in zip mode, src and tgt should be zip files.\n"
+           "  -b <bonus-file>,  Bonus file in addition to src, image mode only.\n"
+           "  --block-limit,    For large zips, split the src and tgt based on the block limit;\n"
+           "                    and generate patches between each pair of pieces. Concatenate "
+           "these\n"
+           "                    patches together and output them into <patch-file>.\n"
+           "  --split-info,     Output the split information (patch_size, tgt_size, src_ranges);\n"
+           "                    zip mode with block-limit only.\n"
+           "  --debug_dir,      Debug directory to put the split srcs and patches, zip mode only.\n"
+           "  -v, --verbose,    Enable verbose logging.";
     return 2;
   }
 
@@ -1538,14 +1556,11 @@
       return 1;
     }
 
-    // TODO save and output the split information so that caller can create split transfer lists
-    // accordingly.
-
     // Compute bsdiff patches for each chunk's data (the uncompressed data, in the case of
     // deflate chunks).
     if (blocks_limit > 0) {
       if (split_info_file.empty()) {
-        printf("split-info path cannot be empty when generating patches with a block-limit.\n");
+        LOG(ERROR) << "split-info path cannot be empty when generating patches with a block-limit";
         return 1;
       }
 
diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp
index 25ba0a1..3682d61 100644
--- a/applypatch/imgpatch.cpp
+++ b/applypatch/imgpatch.cpp
@@ -50,7 +50,7 @@
 // This function is a wrapper of ApplyBSDiffPatch(). It has a custom sink function to deflate the
 // patched data and stream the deflated data to output.
 static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_len,
-                                            const Value* patch, size_t patch_offset,
+                                            const Value& patch, size_t patch_offset,
                                             const char* deflate_header, SinkFn sink, SHA_CTX* ctx) {
   size_t expected_target_length = static_cast<size_t>(Read8(deflate_header + 32));
   int level = Read4(deflate_header + 40);
@@ -135,48 +135,39 @@
 int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data,
                     size_t patch_size, SinkFn sink) {
   Value patch(VAL_BLOB, std::string(reinterpret_cast<const char*>(patch_data), patch_size));
-
-  return ApplyImagePatch(old_data, old_size, &patch, sink, nullptr, nullptr);
+  return ApplyImagePatch(old_data, old_size, patch, sink, nullptr, nullptr);
 }
 
-/*
- * Apply the patch given in 'patch_filename' to the source data given
- * by (old_data, old_size).  Write the patched output to the 'output'
- * file, and update the SHA context with the output data as well.
- * Return 0 on success.
- */
-int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* patch, SinkFn sink,
+int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& patch, SinkFn sink,
                     SHA_CTX* ctx, const Value* bonus_data) {
-  if (patch->data.size() < 12) {
+  if (patch.data.size() < 12) {
     printf("patch too short to contain header\n");
     return -1;
   }
 
-  // IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW.
-  // (IMGDIFF1, which is no longer supported, used CHUNK_NORMAL and
-  // CHUNK_GZIP.)
-  size_t pos = 12;
-  const char* header = &patch->data[0];
-  if (memcmp(header, "IMGDIFF2", 8) != 0) {
+  // IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW. (IMGDIFF1, which is no longer
+  // supported, used CHUNK_NORMAL and CHUNK_GZIP.)
+  const char* const patch_header = patch.data.data();
+  if (memcmp(patch_header, "IMGDIFF2", 8) != 0) {
     printf("corrupt patch file header (magic number)\n");
     return -1;
   }
 
-  int num_chunks = Read4(header + 8);
-
+  int num_chunks = Read4(patch_header + 8);
+  size_t pos = 12;
   for (int i = 0; i < num_chunks; ++i) {
     // each chunk's header record starts with 4 bytes.
-    if (pos + 4 > patch->data.size()) {
+    if (pos + 4 > patch.data.size()) {
       printf("failed to read chunk %d record\n", i);
       return -1;
     }
-    int type = Read4(&patch->data[pos]);
+    int type = Read4(patch_header + pos);
     pos += 4;
 
     if (type == CHUNK_NORMAL) {
-      const char* normal_header = &patch->data[pos];
+      const char* normal_header = patch_header + pos;
       pos += 24;
-      if (pos > patch->data.size()) {
+      if (pos > patch.data.size()) {
         printf("failed to read chunk %d normal header data\n", i);
         return -1;
       }
@@ -194,30 +185,32 @@
         return -1;
       }
     } else if (type == CHUNK_RAW) {
-      const char* raw_header = &patch->data[pos];
+      const char* raw_header = patch_header + pos;
       pos += 4;
-      if (pos > patch->data.size()) {
+      if (pos > patch.data.size()) {
         printf("failed to read chunk %d raw header data\n", i);
         return -1;
       }
 
       size_t data_len = static_cast<size_t>(Read4(raw_header));
 
-      if (pos + data_len > patch->data.size()) {
+      if (pos + data_len > patch.data.size()) {
         printf("failed to read chunk %d raw data\n", i);
         return -1;
       }
-      if (ctx) SHA1_Update(ctx, &patch->data[pos], data_len);
-      if (sink(reinterpret_cast<const unsigned char*>(&patch->data[pos]), data_len) != data_len) {
+      if (ctx) {
+        SHA1_Update(ctx, patch_header + pos, data_len);
+      }
+      if (sink(reinterpret_cast<const unsigned char*>(patch_header + pos), data_len) != data_len) {
         printf("failed to write chunk %d raw data\n", i);
         return -1;
       }
       pos += data_len;
     } else if (type == CHUNK_DEFLATE) {
       // deflate chunks have an additional 60 bytes in their chunk header.
-      const char* deflate_header = &patch->data[pos];
+      const char* deflate_header = patch_header + pos;
       pos += 60;
-      if (pos > patch->data.size()) {
+      if (pos > patch.data.size()) {
         printf("failed to read chunk %d deflate header data\n", i);
         return -1;
       }
diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h
index 2a3b3ef..6d7ffd7 100644
--- a/applypatch/include/applypatch/applypatch.h
+++ b/applypatch/include/applypatch/applypatch.h
@@ -36,16 +36,16 @@
   struct stat st;
 };
 
-// When there isn't enough room on the target filesystem to hold the
-// patched version of the file, we copy the original here and delete
-// it to free up space.  If the expected source file doesn't exist, or
-// is corrupted, we look to see if this file contains the bits we want
-// and use it as the source instead.
-#define CACHE_TEMP_SOURCE "/cache/saved.file"
+// When there isn't enough room on the target filesystem to hold the patched version of the file,
+// we copy the original here and delete it to free up space.  If the expected source file doesn't
+// exist, or is corrupted, we look to see if the cached file contains the bits we want and use it as
+// the source instead.  The default location for the cached source is "/cache/saved.file".
+extern std::string cache_temp_source;
 
 using SinkFn = std::function<size_t(const unsigned char*, size_t)>;
 
 // applypatch.cpp
+
 int ShowLicenses();
 size_t FreeSpaceForFile(const char* filename);
 int CacheSizeCheck(size_t bytes);
@@ -67,15 +67,25 @@
 int SaveFileContents(const char* filename, const FileContents* file);
 
 // bspatch.cpp
+
 void ShowBSDiffLicense();
-int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch,
+
+// Applies the bsdiff-patch given in 'patch' (from offset 'patch_offset' to the end) to the source
+// data given by (old_data, old_size). Writes the patched output through the given 'sink', and
+// updates the SHA-1 context with the output data. Returns 0 on success.
+int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value& patch,
                      size_t patch_offset, SinkFn sink, SHA_CTX* ctx);
 
 // imgpatch.cpp
-int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* patch, SinkFn sink,
+
+// Applies the imgdiff-patch given in 'patch' to the source data given by (old_data, old_size), with
+// the optional bonus data. Writes the patched output through the given 'sink', and updates the
+// SHA-1 context with the output data. Returns 0 on success.
+int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& patch, SinkFn sink,
                     SHA_CTX* ctx, const Value* bonus_data);
 
 // freecache.cpp
+
 int MakeFreeSpaceOnCache(size_t bytes_needed);
 
 #endif
diff --git a/applypatch/include/applypatch/imgdiff_image.h b/applypatch/include/applypatch/imgdiff_image.h
index 00a84f3..0f74420 100644
--- a/applypatch/include/applypatch/imgdiff_image.h
+++ b/applypatch/include/applypatch/imgdiff_image.h
@@ -62,10 +62,7 @@
   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 Dump(size_t index) const;
 
   void SetUncompressedData(std::vector<uint8_t> data);
   bool SetBonusData(const std::vector<uint8_t>& bonus_data);
@@ -140,7 +137,7 @@
 
  private:
   size_t GetHeaderSize() const;
-  size_t WriteHeaderToFd(int fd, size_t offset) const;
+  size_t WriteHeaderToFd(int fd, size_t offset, size_t index) 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.
diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp
index f91446b..aaeffdc 100644
--- a/bootloader_message/bootloader_message.cpp
+++ b/bootloader_message/bootloader_message.cpp
@@ -159,14 +159,8 @@
 
 bool write_bootloader_message(const std::vector<std::string>& options, std::string* err) {
   bootloader_message boot = {};
-  strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
-  strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
-  for (const auto& s : options) {
-    strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery));
-    if (s.back() != '\n') {
-      strlcat(boot.recovery, "\n", sizeof(boot.recovery));
-    }
-  }
+  update_bootloader_message_in_struct(&boot, options);
+
   return write_bootloader_message(boot, err);
 }
 
@@ -175,20 +169,27 @@
   if (!read_bootloader_message(&boot, err)) {
     return false;
   }
+  update_bootloader_message_in_struct(&boot, options);
 
-  // Zero out the entire fields.
-  memset(boot.command, 0, sizeof(boot.command));
-  memset(boot.recovery, 0, sizeof(boot.recovery));
+  return write_bootloader_message(boot, err);
+}
 
-  strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
-  strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
+bool update_bootloader_message_in_struct(bootloader_message* boot,
+                                         const std::vector<std::string>& options) {
+  if (!boot) return false;
+  // Replace the command & recovery fields.
+  memset(boot->command, 0, sizeof(boot->command));
+  memset(boot->recovery, 0, sizeof(boot->recovery));
+
+  strlcpy(boot->command, "boot-recovery", sizeof(boot->command));
+  strlcpy(boot->recovery, "recovery\n", sizeof(boot->recovery));
   for (const auto& s : options) {
-    strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery));
+    strlcat(boot->recovery, s.c_str(), sizeof(boot->recovery));
     if (s.back() != '\n') {
-      strlcat(boot.recovery, "\n", sizeof(boot.recovery));
+      strlcat(boot->recovery, "\n", sizeof(boot->recovery));
     }
   }
-  return write_bootloader_message(boot, err);
+  return true;
 }
 
 bool write_reboot_bootloader(std::string* err) {
diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h
index 2ffbfc9..798f3bb 100644
--- a/bootloader_message/include/bootloader_message/bootloader_message.h
+++ b/bootloader_message/include/bootloader_message/bootloader_message.h
@@ -207,6 +207,11 @@
 // only update the command and recovery fields.
 bool update_bootloader_message(const std::vector<std::string>& options, std::string* err);
 
+// Update bootloader message (boots into recovery with the |options|) in |boot|. Will only update
+// the command and recovery fields.
+bool update_bootloader_message_in_struct(bootloader_message* boot,
+                                         const std::vector<std::string>& options);
+
 // Clear BCB.
 bool clear_bootloader_message(std::string* err);
 
diff --git a/fuse_sdcard_provider.cpp b/fuse_sdcard_provider.cpp
index b0ecf96..46bdf17 100644
--- a/fuse_sdcard_provider.cpp
+++ b/fuse_sdcard_provider.cpp
@@ -14,72 +14,70 @@
  * limitations under the License.
  */
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
+#include "fuse_sdcard_provider.h"
+
 #include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
 #include <unistd.h>
-#include <fcntl.h>
+
+#include <functional>
 
 #include <android-base/file.h>
 
 #include "fuse_sideload.h"
 
 struct file_data {
-    int fd;  // the underlying sdcard file
+  int fd;  // the underlying sdcard file
 
-    uint64_t file_size;
-    uint32_t block_size;
+  uint64_t file_size;
+  uint32_t block_size;
 };
 
-static int read_block_file(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
-    file_data* fd = reinterpret_cast<file_data*>(cookie);
+static int read_block_file(const file_data& fd, uint32_t block, uint8_t* buffer,
+                           uint32_t fetch_size) {
+  off64_t offset = static_cast<off64_t>(block) * fd.block_size;
+  if (TEMP_FAILURE_RETRY(lseek64(fd.fd, offset, SEEK_SET)) == -1) {
+    fprintf(stderr, "seek on sdcard failed: %s\n", strerror(errno));
+    return -EIO;
+  }
 
-    off64_t offset = ((off64_t) block) * fd->block_size;
-    if (TEMP_FAILURE_RETRY(lseek64(fd->fd, offset, SEEK_SET)) == -1) {
-        fprintf(stderr, "seek on sdcard failed: %s\n", strerror(errno));
-        return -EIO;
-    }
+  if (!android::base::ReadFully(fd.fd, buffer, fetch_size)) {
+    fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno));
+    return -EIO;
+  }
 
-    if (!android::base::ReadFully(fd->fd, buffer, fetch_size)) {
-        fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno));
-        return -EIO;
-    }
-
-    return 0;
-}
-
-static void close_file(void* cookie) {
-    file_data* fd = reinterpret_cast<file_data*>(cookie);
-    close(fd->fd);
+  return 0;
 }
 
 bool start_sdcard_fuse(const char* path) {
-    struct stat sb;
-    if (stat(path, &sb) == -1) {
-        fprintf(stderr, "failed to stat %s: %s\n", path, strerror(errno));
-        return false;
-    }
+  struct stat sb;
+  if (stat(path, &sb) == -1) {
+    fprintf(stderr, "failed to stat %s: %s\n", path, strerror(errno));
+    return false;
+  }
 
-    file_data fd;
-    fd.fd = open(path, O_RDONLY);
-    if (fd.fd == -1) {
-        fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno));
-        return false;
-    }
-    fd.file_size = sb.st_size;
-    fd.block_size = 65536;
+  file_data fd;
+  fd.fd = open(path, O_RDONLY);
+  if (fd.fd == -1) {
+    fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno));
+    return false;
+  }
+  fd.file_size = sb.st_size;
+  fd.block_size = 65536;
 
-    provider_vtab vtab;
-    vtab.read_block = read_block_file;
-    vtab.close = close_file;
+  provider_vtab vtab;
+  vtab.read_block = std::bind(&read_block_file, fd, std::placeholders::_1, std::placeholders::_2,
+                              std::placeholders::_3);
+  vtab.close = [&fd]() { close(fd.fd); };
 
-    // The installation process expects to find the sdcard unmounted.
-    // Unmount it with MNT_DETACH so that our open file continues to
-    // work but new references see it as unmounted.
-    umount2("/sdcard", MNT_DETACH);
+  // The installation process expects to find the sdcard unmounted. Unmount it with MNT_DETACH so
+  // that our open file continues to work but new references see it as unmounted.
+  umount2("/sdcard", MNT_DETACH);
 
-    return run_fuse_sideload(&vtab, &fd, fd.file_size, fd.block_size) == 0;
+  return run_fuse_sideload(vtab, fd.file_size, fd.block_size) == 0;
 }
diff --git a/fuse_sideload.cpp b/fuse_sideload.cpp
index 219374f..1c7e98f 100644
--- a/fuse_sideload.cpp
+++ b/fuse_sideload.cpp
@@ -41,337 +41,310 @@
 // two files is implemented.  In particular, you can't opendir() or
 // readdir() on the "/sideload" directory; ls on it won't work.
 
-#include <ctype.h>
-#include <dirent.h>
+#include "fuse_sideload.h"
+
 #include <errno.h>
 #include <fcntl.h>
-#include <limits.h>
+#include <limits.h>  // PATH_MAX
 #include <linux/fuse.h>
-#include <pthread.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/inotify.h>
 #include <sys/mount.h>
-#include <sys/param.h>
-#include <sys/resource.h>
+#include <sys/param.h>  // MIN
 #include <sys/stat.h>
-#include <sys/statfs.h>
-#include <sys/time.h>
 #include <sys/uio.h>
 #include <unistd.h>
 
+#include <array>
 #include <string>
+#include <vector>
 
 #include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
 #include <openssl/sha.h>
 
-#include "fuse_sideload.h"
+static constexpr uint64_t PACKAGE_FILE_ID = FUSE_ROOT_ID + 1;
+static constexpr uint64_t EXIT_FLAG_ID = FUSE_ROOT_ID + 2;
 
-#define PACKAGE_FILE_ID   (FUSE_ROOT_ID+1)
-#define EXIT_FLAG_ID      (FUSE_ROOT_ID+2)
+static constexpr int NO_STATUS = 1;
+static constexpr int NO_STATUS_EXIT = 2;
 
-#define NO_STATUS         1
-#define NO_STATUS_EXIT    2
+using SHA256Digest = std::array<uint8_t, SHA256_DIGEST_LENGTH>;
 
 struct fuse_data {
-    int ffd;   // file descriptor for the fuse socket
+  android::base::unique_fd ffd;  // file descriptor for the fuse socket
 
-    struct provider_vtab* vtab;
-    void* cookie;
+  provider_vtab vtab;
 
-    uint64_t file_size;     // bytes
+  uint64_t file_size;  // bytes
 
-    uint32_t block_size;    // block size that the adb host is using to send the file to us
-    uint32_t file_blocks;   // file size in block_size blocks
+  uint32_t block_size;   // block size that the adb host is using to send the file to us
+  uint32_t file_blocks;  // file size in block_size blocks
 
-    uid_t uid;
-    gid_t gid;
+  uid_t uid;
+  gid_t gid;
 
-    uint32_t curr_block;    // cache the block most recently read from the host
-    uint8_t* block_data;
+  uint32_t curr_block;  // cache the block most recently read from the host
+  uint8_t* block_data;
 
-    uint8_t* extra_block;   // another block of storage for reads that
-                            // span two blocks
+  uint8_t* extra_block;  // another block of storage for reads that span two blocks
 
-    uint8_t* hashes;        // SHA-256 hash of each block (all zeros
-                            // if block hasn't been read yet)
+  std::vector<SHA256Digest>
+      hashes;  // SHA-256 hash of each block (all zeros if block hasn't been read yet)
 };
 
-static void fuse_reply(struct fuse_data* fd, __u64 unique, const void *data, size_t len)
-{
-    struct fuse_out_header hdr;
-    struct iovec vec[2];
-    int res;
+static void fuse_reply(const fuse_data* fd, uint64_t unique, const void* data, size_t len) {
+  fuse_out_header hdr;
+  hdr.len = len + sizeof(hdr);
+  hdr.error = 0;
+  hdr.unique = unique;
 
-    hdr.len = len + sizeof(hdr);
-    hdr.error = 0;
-    hdr.unique = unique;
+  struct iovec vec[2];
+  vec[0].iov_base = &hdr;
+  vec[0].iov_len = sizeof(hdr);
+  vec[1].iov_base = const_cast<void*>(data);
+  vec[1].iov_len = len;
 
-    vec[0].iov_base = &hdr;
-    vec[0].iov_len = sizeof(hdr);
-    vec[1].iov_base = /* const_cast */(void*)(data);
-    vec[1].iov_len = len;
-
-    res = writev(fd->ffd, vec, 2);
-    if (res < 0) {
-        printf("*** REPLY FAILED *** %s\n", strerror(errno));
-    }
+  int res = writev(fd->ffd, vec, 2);
+  if (res == -1) {
+    printf("*** REPLY FAILED *** %s\n", strerror(errno));
+  }
 }
 
-static int handle_init(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) {
-    const struct fuse_init_in* req = reinterpret_cast<const struct fuse_init_in*>(data);
-    struct fuse_init_out out;
-    size_t fuse_struct_size;
+static int handle_init(void* data, fuse_data* fd, const fuse_in_header* hdr) {
+  const fuse_init_in* req = static_cast<const fuse_init_in*>(data);
 
+  // Kernel 2.6.16 is the first stable kernel with struct fuse_init_out defined (fuse version 7.6).
+  // The structure is the same from 7.6 through 7.22. Beginning with 7.23, the structure increased
+  // in size and added new parameters.
+  if (req->major != FUSE_KERNEL_VERSION || req->minor < 6) {
+    printf("Fuse kernel version mismatch: Kernel version %d.%d, Expected at least %d.6", req->major,
+           req->minor, FUSE_KERNEL_VERSION);
+    return -1;
+  }
 
-    /* Kernel 2.6.16 is the first stable kernel with struct fuse_init_out
-     * defined (fuse version 7.6). The structure is the same from 7.6 through
-     * 7.22. Beginning with 7.23, the structure increased in size and added
-     * new parameters.
-     */
-    if (req->major != FUSE_KERNEL_VERSION || req->minor < 6) {
-        printf("Fuse kernel version mismatch: Kernel version %d.%d, Expected at least %d.6",
-               req->major, req->minor, FUSE_KERNEL_VERSION);
-        return -1;
-    }
-
-    out.minor = MIN(req->minor, FUSE_KERNEL_MINOR_VERSION);
-    fuse_struct_size = sizeof(out);
+  fuse_init_out out;
+  out.minor = MIN(req->minor, FUSE_KERNEL_MINOR_VERSION);
+  size_t fuse_struct_size = sizeof(out);
 #if defined(FUSE_COMPAT_22_INIT_OUT_SIZE)
-    /* FUSE_KERNEL_VERSION >= 23. */
+  /* FUSE_KERNEL_VERSION >= 23. */
 
-    /* If the kernel only works on minor revs older than or equal to 22,
-     * then use the older structure size since this code only uses the 7.22
-     * version of the structure. */
-    if (req->minor <= 22) {
-        fuse_struct_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
-    }
+  // If the kernel only works on minor revs older than or equal to 22, then use the older structure
+  // size since this code only uses the 7.22 version of the structure.
+  if (req->minor <= 22) {
+    fuse_struct_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
+  }
 #endif
 
-    out.major = FUSE_KERNEL_VERSION;
-    out.max_readahead = req->max_readahead;
-    out.flags = 0;
-    out.max_background = 32;
-    out.congestion_threshold = 32;
-    out.max_write = 4096;
-    fuse_reply(fd, hdr->unique, &out, fuse_struct_size);
+  out.major = FUSE_KERNEL_VERSION;
+  out.max_readahead = req->max_readahead;
+  out.flags = 0;
+  out.max_background = 32;
+  out.congestion_threshold = 32;
+  out.max_write = 4096;
+  fuse_reply(fd, hdr->unique, &out, fuse_struct_size);
 
-    return NO_STATUS;
+  return NO_STATUS;
 }
 
-static void fill_attr(struct fuse_attr* attr, struct fuse_data* fd,
-                      uint64_t nodeid, uint64_t size, uint32_t mode) {
-    memset(attr, 0, sizeof(*attr));
-    attr->nlink = 1;
-    attr->uid = fd->uid;
-    attr->gid = fd->gid;
-    attr->blksize = 4096;
+static void fill_attr(fuse_attr* attr, const fuse_data* fd, uint64_t nodeid, uint64_t size,
+                      uint32_t mode) {
+  *attr = {};
+  attr->nlink = 1;
+  attr->uid = fd->uid;
+  attr->gid = fd->gid;
+  attr->blksize = 4096;
 
-    attr->ino = nodeid;
-    attr->size = size;
-    attr->blocks = (size == 0) ? 0 : (((size-1) / attr->blksize) + 1);
-    attr->mode = mode;
+  attr->ino = nodeid;
+  attr->size = size;
+  attr->blocks = (size == 0) ? 0 : (((size - 1) / attr->blksize) + 1);
+  attr->mode = mode;
 }
 
-static int handle_getattr(void* /* data */, struct fuse_data* fd, const struct fuse_in_header* hdr) {
-    struct fuse_attr_out out;
-    memset(&out, 0, sizeof(out));
-    out.attr_valid = 10;
+static int handle_getattr(void* /* data */, const fuse_data* fd, const fuse_in_header* hdr) {
+  fuse_attr_out out = {};
+  out.attr_valid = 10;
 
-    if (hdr->nodeid == FUSE_ROOT_ID) {
-        fill_attr(&(out.attr), fd, hdr->nodeid, 4096, S_IFDIR | 0555);
-    } else if (hdr->nodeid == PACKAGE_FILE_ID) {
-        fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444);
-    } else if (hdr->nodeid == EXIT_FLAG_ID) {
-        fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0);
-    } else {
-        return -ENOENT;
-    }
+  if (hdr->nodeid == FUSE_ROOT_ID) {
+    fill_attr(&(out.attr), fd, hdr->nodeid, 4096, S_IFDIR | 0555);
+  } else if (hdr->nodeid == PACKAGE_FILE_ID) {
+    fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444);
+  } else if (hdr->nodeid == EXIT_FLAG_ID) {
+    fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0);
+  } else {
+    return -ENOENT;
+  }
 
-    fuse_reply(fd, hdr->unique, &out, sizeof(out));
-    return (hdr->nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS;
+  fuse_reply(fd, hdr->unique, &out, sizeof(out));
+  return (hdr->nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS;
 }
 
-static int handle_lookup(void* data, struct fuse_data* fd,
-                         const struct fuse_in_header* hdr) {
-    struct fuse_entry_out out;
-    memset(&out, 0, sizeof(out));
-    out.entry_valid = 10;
-    out.attr_valid = 10;
+static int handle_lookup(void* data, const fuse_data* fd, const fuse_in_header* hdr) {
+  if (data == nullptr) return -ENOENT;
 
-    if (strncmp(FUSE_SIDELOAD_HOST_FILENAME, reinterpret_cast<const char*>(data),
-                sizeof(FUSE_SIDELOAD_HOST_FILENAME)) == 0) {
-        out.nodeid = PACKAGE_FILE_ID;
-        out.generation = PACKAGE_FILE_ID;
-        fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444);
-    } else if (strncmp(FUSE_SIDELOAD_HOST_EXIT_FLAG, reinterpret_cast<const char*>(data),
-                       sizeof(FUSE_SIDELOAD_HOST_EXIT_FLAG)) == 0) {
-        out.nodeid = EXIT_FLAG_ID;
-        out.generation = EXIT_FLAG_ID;
-        fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0);
-    } else {
-        return -ENOENT;
-    }
+  fuse_entry_out out = {};
+  out.entry_valid = 10;
+  out.attr_valid = 10;
 
-    fuse_reply(fd, hdr->unique, &out, sizeof(out));
-    return (out.nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS;
+  std::string filename(static_cast<const char*>(data));
+  if (filename == FUSE_SIDELOAD_HOST_FILENAME) {
+    out.nodeid = PACKAGE_FILE_ID;
+    out.generation = PACKAGE_FILE_ID;
+    fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444);
+  } else if (filename == FUSE_SIDELOAD_HOST_EXIT_FLAG) {
+    out.nodeid = EXIT_FLAG_ID;
+    out.generation = EXIT_FLAG_ID;
+    fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0);
+  } else {
+    return -ENOENT;
+  }
+
+  fuse_reply(fd, hdr->unique, &out, sizeof(out));
+  return (out.nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS;
 }
 
-static int handle_open(void* /* data */, struct fuse_data* fd, const struct fuse_in_header* hdr) {
-    if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM;
-    if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT;
+static int handle_open(void* /* data */, const fuse_data* fd, const fuse_in_header* hdr) {
+  if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM;
+  if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT;
 
-    struct fuse_open_out out;
-    memset(&out, 0, sizeof(out));
-    out.fh = 10;  // an arbitrary number; we always use the same handle
-    fuse_reply(fd, hdr->unique, &out, sizeof(out));
-    return NO_STATUS;
+  fuse_open_out out = {};
+  out.fh = 10;  // an arbitrary number; we always use the same handle
+  fuse_reply(fd, hdr->unique, &out, sizeof(out));
+  return NO_STATUS;
 }
 
-static int handle_flush(void* /* data */, struct fuse_data* /* fd */,
-                        const struct fuse_in_header* /* hdr */) {
-    return 0;
+static int handle_flush(void* /* data */, fuse_data* /* fd */, const fuse_in_header* /* hdr */) {
+  return 0;
 }
 
-static int handle_release(void* /* data */, struct fuse_data* /* fd */,
-                          const struct fuse_in_header* /* hdr */) {
-    return 0;
+static int handle_release(void* /* data */, fuse_data* /* fd */, const fuse_in_header* /* hdr */) {
+  return 0;
 }
 
 // Fetch a block from the host into fd->curr_block and fd->block_data.
 // Returns 0 on successful fetch, negative otherwise.
-static int fetch_block(struct fuse_data* fd, uint32_t block) {
-    if (block == fd->curr_block) {
-        return 0;
-    }
-
-    if (block >= fd->file_blocks) {
-        memset(fd->block_data, 0, fd->block_size);
-        fd->curr_block = block;
-        return 0;
-    }
-
-    size_t fetch_size = fd->block_size;
-    if (block * fd->block_size + fetch_size > fd->file_size) {
-        // If we're reading the last (partial) block of the file,
-        // expect a shorter response from the host, and pad the rest
-        // of the block with zeroes.
-        fetch_size = fd->file_size - (block * fd->block_size);
-        memset(fd->block_data + fetch_size, 0, fd->block_size - fetch_size);
-    }
-
-    int result = fd->vtab->read_block(fd->cookie, block, fd->block_data, fetch_size);
-    if (result < 0) return result;
-
-    fd->curr_block = block;
-
-    // Verify the hash of the block we just got from the host.
-    //
-    // - If the hash of the just-received data matches the stored hash
-    //   for the block, accept it.
-    // - If the stored hash is all zeroes, store the new hash and
-    //   accept the block (this is the first time we've read this
-    //   block).
-    // - Otherwise, return -EINVAL for the read.
-
-    uint8_t hash[SHA256_DIGEST_LENGTH];
-    SHA256(fd->block_data, fd->block_size, hash);
-    uint8_t* blockhash = fd->hashes + block * SHA256_DIGEST_LENGTH;
-    if (memcmp(hash, blockhash, SHA256_DIGEST_LENGTH) == 0) {
-        return 0;
-    }
-
-    int i;
-    for (i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
-        if (blockhash[i] != 0) {
-            fd->curr_block = -1;
-            return -EIO;
-        }
-    }
-
-    memcpy(blockhash, hash, SHA256_DIGEST_LENGTH);
+static int fetch_block(fuse_data* fd, uint32_t block) {
+  if (block == fd->curr_block) {
     return 0;
+  }
+
+  if (block >= fd->file_blocks) {
+    memset(fd->block_data, 0, fd->block_size);
+    fd->curr_block = block;
+    return 0;
+  }
+
+  size_t fetch_size = fd->block_size;
+  if (block * fd->block_size + fetch_size > fd->file_size) {
+    // If we're reading the last (partial) block of the file, expect a shorter response from the
+    // host, and pad the rest of the block with zeroes.
+    fetch_size = fd->file_size - (block * fd->block_size);
+    memset(fd->block_data + fetch_size, 0, fd->block_size - fetch_size);
+  }
+
+  int result = fd->vtab.read_block(block, fd->block_data, fetch_size);
+  if (result < 0) return result;
+
+  fd->curr_block = block;
+
+  // Verify the hash of the block we just got from the host.
+  //
+  // - If the hash of the just-received data matches the stored hash for the block, accept it.
+  // - If the stored hash is all zeroes, store the new hash and accept the block (this is the first
+  //   time we've read this block).
+  // - Otherwise, return -EINVAL for the read.
+
+  SHA256Digest hash;
+  SHA256(fd->block_data, fd->block_size, hash.data());
+
+  const SHA256Digest& blockhash = fd->hashes[block];
+  if (hash == blockhash) {
+    return 0;
+  }
+
+  for (uint8_t i : blockhash) {
+    if (i != 0) {
+      fd->curr_block = -1;
+      return -EIO;
+    }
+  }
+
+  fd->hashes[block] = hash;
+  return 0;
 }
 
-static int handle_read(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) {
-    const struct fuse_read_in* req = reinterpret_cast<const struct fuse_read_in*>(data);
-    struct fuse_out_header outhdr;
-    struct iovec vec[3];
-    int vec_used;
-    int result;
+static int handle_read(void* data, fuse_data* fd, const fuse_in_header* hdr) {
+  if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT;
 
-    if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT;
+  const fuse_read_in* req = static_cast<const fuse_read_in*>(data);
+  uint64_t offset = req->offset;
+  uint32_t size = req->size;
 
-    uint64_t offset = req->offset;
-    uint32_t size = req->size;
+  // The docs on the fuse kernel interface are vague about what to do when a read request extends
+  // past the end of the file. We can return a short read -- the return structure does include a
+  // length field -- but in testing that caused the program using the file to segfault. (I
+  // speculate that this is due to the reading program accessing it via mmap; maybe mmap dislikes
+  // when you return something short of a whole page?) To fix this we zero-pad reads that extend
+  // past the end of the file so we're always returning exactly as many bytes as were requested.
+  // (Users of the mapped file have to know its real length anyway.)
 
-    // The docs on the fuse kernel interface are vague about what to
-    // do when a read request extends past the end of the file.  We
-    // can return a short read -- the return structure does include a
-    // length field -- but in testing that caused the program using
-    // the file to segfault.  (I speculate that this is due to the
-    // reading program accessing it via mmap; maybe mmap dislikes when
-    // you return something short of a whole page?)  To fix this we
-    // zero-pad reads that extend past the end of the file so we're
-    // always returning exactly as many bytes as were requested.
-    // (Users of the mapped file have to know its real length anyway.)
+  fuse_out_header outhdr;
+  outhdr.len = sizeof(outhdr) + size;
+  outhdr.error = 0;
+  outhdr.unique = hdr->unique;
 
-    outhdr.len = sizeof(outhdr) + size;
-    outhdr.error = 0;
-    outhdr.unique = hdr->unique;
-    vec[0].iov_base = &outhdr;
-    vec[0].iov_len = sizeof(outhdr);
+  struct iovec vec[3];
+  vec[0].iov_base = &outhdr;
+  vec[0].iov_len = sizeof(outhdr);
 
-    uint32_t block = offset / fd->block_size;
-    result = fetch_block(fd, block);
+  uint32_t block = offset / fd->block_size;
+  int result = fetch_block(fd, block);
+  if (result != 0) return result;
+
+  // Two cases:
+  //
+  //   - the read request is entirely within this block. In this case we can reply immediately.
+  //
+  //   - the read request goes over into the next block. Note that since we mount the filesystem
+  //     with max_read=block_size, a read can never span more than two blocks. In this case we copy
+  //     the block to extra_block and issue a fetch for the following block.
+
+  uint32_t block_offset = offset - (block * fd->block_size);
+
+  int vec_used;
+  if (size + block_offset <= fd->block_size) {
+    // First case: the read fits entirely in the first block.
+
+    vec[1].iov_base = fd->block_data + block_offset;
+    vec[1].iov_len = size;
+    vec_used = 2;
+  } else {
+    // Second case: the read spills over into the next block.
+
+    memcpy(fd->extra_block, fd->block_data + block_offset, fd->block_size - block_offset);
+    vec[1].iov_base = fd->extra_block;
+    vec[1].iov_len = fd->block_size - block_offset;
+
+    result = fetch_block(fd, block + 1);
     if (result != 0) return result;
+    vec[2].iov_base = fd->block_data;
+    vec[2].iov_len = size - vec[1].iov_len;
+    vec_used = 3;
+  }
 
-    // Two cases:
-    //
-    //   - the read request is entirely within this block.  In this
-    //     case we can reply immediately.
-    //
-    //   - the read request goes over into the next block.  Note that
-    //     since we mount the filesystem with max_read=block_size, a
-    //     read can never span more than two blocks.  In this case we
-    //     copy the block to extra_block and issue a fetch for the
-    //     following block.
-
-    uint32_t block_offset = offset - (block * fd->block_size);
-
-    if (size + block_offset <= fd->block_size) {
-        // First case: the read fits entirely in the first block.
-
-        vec[1].iov_base = fd->block_data + block_offset;
-        vec[1].iov_len = size;
-        vec_used = 2;
-    } else {
-        // Second case: the read spills over into the next block.
-
-        memcpy(fd->extra_block, fd->block_data + block_offset,
-               fd->block_size - block_offset);
-        vec[1].iov_base = fd->extra_block;
-        vec[1].iov_len = fd->block_size - block_offset;
-
-        result = fetch_block(fd, block+1);
-        if (result != 0) return result;
-        vec[2].iov_base = fd->block_data;
-        vec[2].iov_len = size - vec[1].iov_len;
-        vec_used = 3;
-    }
-
-    if (writev(fd->ffd, vec, vec_used) < 0) {
-        printf("*** READ REPLY FAILED: %s ***\n", strerror(errno));
-    }
-    return NO_STATUS;
+  if (writev(fd->ffd, vec, vec_used) == -1) {
+    printf("*** READ REPLY FAILED: %s ***\n", strerror(errno));
+  }
+  return NO_STATUS;
 }
 
-int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_size,
-                      uint32_t block_size) {
+int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t block_size,
+                      const char* mount_point) {
   // If something's already mounted on our mountpoint, try to remove it. (Mostly in case of a
   // previous abnormal exit.)
-  umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_FORCE);
+  umount2(mount_point, MNT_FORCE);
 
   // fs/fuse/inode.c in kernel code uses the greater of 4096 and the passed-in max_read.
   if (block_size < 4096) {
@@ -383,9 +356,8 @@
     return -1;
   }
 
-  struct fuse_data fd = {};
+  fuse_data fd = {};
   fd.vtab = vtab;
-  fd.cookie = cookie;
   fd.file_size = file_size;
   fd.block_size = block_size;
   fd.file_blocks = (file_size == 0) ? 0 : (((file_size - 1) / block_size) + 1);
@@ -397,33 +369,27 @@
     goto done;
   }
 
-  fd.hashes = (uint8_t*)calloc(fd.file_blocks, SHA256_DIGEST_LENGTH);
-  if (fd.hashes == NULL) {
-    fprintf(stderr, "failed to allocate %d bites for hashes\n",
-            fd.file_blocks * SHA256_DIGEST_LENGTH);
-    result = -1;
-    goto done;
-  }
-
+  // All hashes will be zero-initialized.
+  fd.hashes.resize(fd.file_blocks);
   fd.uid = getuid();
   fd.gid = getgid();
 
   fd.curr_block = -1;
-  fd.block_data = (uint8_t*)malloc(block_size);
-  if (fd.block_data == NULL) {
+  fd.block_data = static_cast<uint8_t*>(malloc(block_size));
+  if (fd.block_data == nullptr) {
     fprintf(stderr, "failed to allocate %d bites for block_data\n", block_size);
     result = -1;
     goto done;
   }
-  fd.extra_block = (uint8_t*)malloc(block_size);
-  if (fd.extra_block == NULL) {
+  fd.extra_block = static_cast<uint8_t*>(malloc(block_size));
+  if (fd.extra_block == nullptr) {
     fprintf(stderr, "failed to allocate %d bites for extra_block\n", block_size);
     result = -1;
     goto done;
   }
 
-  fd.ffd = open("/dev/fuse", O_RDWR);
-  if (fd.ffd < 0) {
+  fd.ffd.reset(open("/dev/fuse", O_RDWR));
+  if (!fd.ffd) {
     perror("open /dev/fuse");
     result = -1;
     goto done;
@@ -431,18 +397,18 @@
 
   {
     std::string opts = android::base::StringPrintf(
-        "fd=%d,user_id=%d,group_id=%d,max_read=%u,allow_other,rootmode=040000", fd.ffd, fd.uid,
-        fd.gid, block_size);
+        "fd=%d,user_id=%d,group_id=%d,max_read=%u,allow_other,rootmode=040000", fd.ffd.get(),
+        fd.uid, fd.gid, block_size);
 
-    result = mount("/dev/fuse", FUSE_SIDELOAD_HOST_MOUNTPOINT, "fuse",
-                   MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC, opts.c_str());
-    if (result < 0) {
+    result = mount("/dev/fuse", mount_point, "fuse", MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC,
+                   opts.c_str());
+    if (result == -1) {
       perror("mount");
       goto done;
     }
   }
 
-  uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX * 8];
+  uint8_t request_buffer[sizeof(fuse_in_header) + PATH_MAX * 8];
   for (;;) {
     ssize_t len = TEMP_FAILURE_RETRY(read(fd.ffd, request_buffer, sizeof(request_buffer)));
     if (len == -1) {
@@ -454,13 +420,13 @@
       continue;
     }
 
-    if (static_cast<size_t>(len) < sizeof(struct fuse_in_header)) {
+    if (static_cast<size_t>(len) < sizeof(fuse_in_header)) {
       fprintf(stderr, "request too short: len=%zd\n", len);
       continue;
     }
 
-    struct fuse_in_header* hdr = reinterpret_cast<struct fuse_in_header*>(request_buffer);
-    void* data = request_buffer + sizeof(struct fuse_in_header);
+    fuse_in_header* hdr = reinterpret_cast<fuse_in_header*>(request_buffer);
+    void* data = request_buffer + sizeof(fuse_in_header);
 
     result = -ENOSYS;
 
@@ -504,7 +470,7 @@
     }
 
     if (result != NO_STATUS) {
-      struct fuse_out_header outhdr;
+      fuse_out_header outhdr;
       outhdr.len = sizeof(outhdr);
       outhdr.error = result;
       outhdr.unique = hdr->unique;
@@ -513,15 +479,12 @@
   }
 
 done:
-  fd.vtab->close(fd.cookie);
+  fd.vtab.close();
 
-  result = umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_DETACH);
-  if (result < 0) {
-    printf("fuse_sideload umount failed: %s\n", strerror(errno));
+  if (umount2(mount_point, MNT_DETACH) == -1) {
+    fprintf(stderr, "fuse_sideload umount failed: %s\n", strerror(errno));
   }
 
-  if (fd.ffd) close(fd.ffd);
-  free(fd.hashes);
   free(fd.block_data);
   free(fd.extra_block);
 
diff --git a/fuse_sideload.h b/fuse_sideload.h
index c0b16ef..1b34cbd 100644
--- a/fuse_sideload.h
+++ b/fuse_sideload.h
@@ -17,22 +17,24 @@
 #ifndef __FUSE_SIDELOAD_H
 #define __FUSE_SIDELOAD_H
 
-// define the filenames created by the sideload FUSE filesystem
-#define FUSE_SIDELOAD_HOST_MOUNTPOINT "/sideload"
-#define FUSE_SIDELOAD_HOST_FILENAME "package.zip"
-#define FUSE_SIDELOAD_HOST_PATHNAME (FUSE_SIDELOAD_HOST_MOUNTPOINT "/" FUSE_SIDELOAD_HOST_FILENAME)
-#define FUSE_SIDELOAD_HOST_EXIT_FLAG "exit"
-#define FUSE_SIDELOAD_HOST_EXIT_PATHNAME (FUSE_SIDELOAD_HOST_MOUNTPOINT "/" FUSE_SIDELOAD_HOST_EXIT_FLAG)
+#include <functional>
+
+// Define the filenames created by the sideload FUSE filesystem.
+static constexpr const char* FUSE_SIDELOAD_HOST_MOUNTPOINT = "/sideload";
+static constexpr const char* FUSE_SIDELOAD_HOST_FILENAME = "package.zip";
+static constexpr const char* FUSE_SIDELOAD_HOST_PATHNAME = "/sideload/package.zip";
+static constexpr const char* FUSE_SIDELOAD_HOST_EXIT_FLAG = "exit";
+static constexpr const char* FUSE_SIDELOAD_HOST_EXIT_PATHNAME = "/sideload/exit";
 
 struct provider_vtab {
-    // read a block
-    int (*read_block)(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size);
+  // read a block
+  std::function<int(uint32_t block, uint8_t* buffer, uint32_t fetch_size)> read_block;
 
-    // close down
-    void (*close)(void* cookie);
+  // close down
+  std::function<void(void)> close;
 };
 
-int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
-                      uint64_t file_size, uint32_t block_size);
+int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t block_size,
+                      const char* mount_point = FUSE_SIDELOAD_HOST_MOUNTPOINT);
 
 #endif
diff --git a/minadbd/Android.mk b/minadbd/Android.mk
index 803171d..50e3b34 100644
--- a/minadbd/Android.mk
+++ b/minadbd/Android.mk
@@ -16,10 +16,9 @@
 
 minadbd_cflags := \
     -Wall -Werror \
-    -Wno-missing-field-initializers \
     -DADB_HOST=0 \
 
-# libadbd (static library)
+# libminadbd (static library)
 # ===============================
 include $(CLEAR_VARS)
 
@@ -30,7 +29,6 @@
 
 LOCAL_MODULE := libminadbd
 LOCAL_CFLAGS := $(minadbd_cflags)
-LOCAL_CONLY_FLAGS := -Wimplicit-function-declaration
 LOCAL_C_INCLUDES := bootable/recovery system/core/adb
 LOCAL_WHOLE_STATIC_LIBRARIES := libadbd
 LOCAL_STATIC_LIBRARIES := libcrypto libbase
@@ -46,7 +44,12 @@
 LOCAL_SRC_FILES := fuse_adb_provider_test.cpp
 LOCAL_CFLAGS := $(minadbd_cflags)
 LOCAL_C_INCLUDES := $(LOCAL_PATH) system/core/adb
-LOCAL_STATIC_LIBRARIES := libminadbd
-LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
+LOCAL_STATIC_LIBRARIES := \
+    libBionicGtestMain \
+    libminadbd
+LOCAL_SHARED_LIBRARIES := \
+    liblog \
+    libbase \
+    libcutils
 
 include $(BUILD_NATIVE_TEST)
diff --git a/minadbd/fuse_adb_provider.cpp b/minadbd/fuse_adb_provider.cpp
index 0f4c256..9bd3f23 100644
--- a/minadbd/fuse_adb_provider.cpp
+++ b/minadbd/fuse_adb_provider.cpp
@@ -14,46 +14,43 @@
  * limitations under the License.
  */
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
+#include "fuse_adb_provider.h"
+
 #include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <functional>
 
 #include "adb.h"
 #include "adb_io.h"
-#include "fuse_adb_provider.h"
 #include "fuse_sideload.h"
 
-int read_block_adb(void* data, uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
-    adb_data* ad = reinterpret_cast<adb_data*>(data);
+int read_block_adb(const adb_data& ad, uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
+  if (!WriteFdFmt(ad.sfd, "%08u", block)) {
+    fprintf(stderr, "failed to write to adb host: %s\n", strerror(errno));
+    return -EIO;
+  }
 
-    if (!WriteFdFmt(ad->sfd, "%08u", block)) {
-        fprintf(stderr, "failed to write to adb host: %s\n", strerror(errno));
-        return -EIO;
-    }
+  if (!ReadFdExactly(ad.sfd, buffer, fetch_size)) {
+    fprintf(stderr, "failed to read from adb host: %s\n", strerror(errno));
+    return -EIO;
+  }
 
-    if (!ReadFdExactly(ad->sfd, buffer, fetch_size)) {
-        fprintf(stderr, "failed to read from adb host: %s\n", strerror(errno));
-        return -EIO;
-    }
-
-    return 0;
-}
-
-static void close_adb(void* data) {
-    adb_data* ad = reinterpret_cast<adb_data*>(data);
-    WriteFdExactly(ad->sfd, "DONEDONE");
+  return 0;
 }
 
 int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size) {
-    adb_data ad;
-    ad.sfd = sfd;
-    ad.file_size = file_size;
-    ad.block_size = block_size;
+  adb_data ad;
+  ad.sfd = sfd;
+  ad.file_size = file_size;
+  ad.block_size = block_size;
 
-    provider_vtab vtab;
-    vtab.read_block = read_block_adb;
-    vtab.close = close_adb;
+  provider_vtab vtab;
+  vtab.read_block = std::bind(read_block_adb, ad, std::placeholders::_1, std::placeholders::_2,
+                              std::placeholders::_3);
+  vtab.close = [&ad]() { WriteFdExactly(ad.sfd, "DONEDONE"); };
 
-    return run_fuse_sideload(&vtab, &ad, file_size, block_size);
+  return run_fuse_sideload(vtab, file_size, block_size);
 }
diff --git a/minadbd/fuse_adb_provider.h b/minadbd/fuse_adb_provider.h
index 9941709..36d86d5 100644
--- a/minadbd/fuse_adb_provider.h
+++ b/minadbd/fuse_adb_provider.h
@@ -20,13 +20,13 @@
 #include <stdint.h>
 
 struct adb_data {
-    int sfd;  // file descriptor for the adb channel
+  int sfd;  // file descriptor for the adb channel
 
-    uint64_t file_size;
-    uint32_t block_size;
+  uint64_t file_size;
+  uint32_t block_size;
 };
 
-int read_block_adb(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size);
+int read_block_adb(const adb_data& ad, uint32_t block, uint8_t* buffer, uint32_t fetch_size);
 int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size);
 
 #endif
diff --git a/minadbd/fuse_adb_provider_test.cpp b/minadbd/fuse_adb_provider_test.cpp
index 31be2a6..00250e5 100644
--- a/minadbd/fuse_adb_provider_test.cpp
+++ b/minadbd/fuse_adb_provider_test.cpp
@@ -46,8 +46,8 @@
 
   uint32_t block = 1234U;
   const char expected_block[] = "00001234";
-  ASSERT_EQ(0, read_block_adb(static_cast<void*>(&data), block,
-                              reinterpret_cast<uint8_t*>(block_data), sizeof(expected_data) - 1));
+  ASSERT_EQ(0, read_block_adb(data, block, reinterpret_cast<uint8_t*>(block_data),
+                              sizeof(expected_data) - 1));
 
   // Check that read_block_adb requested the right block.
   char block_req[sizeof(expected_block)] = {};
@@ -84,7 +84,7 @@
   signal(SIGPIPE, SIG_IGN);
 
   char buf[1];
-  ASSERT_EQ(-EIO, read_block_adb(static_cast<void*>(&data), 0, reinterpret_cast<uint8_t*>(buf), 1));
+  ASSERT_EQ(-EIO, read_block_adb(data, 0, reinterpret_cast<uint8_t*>(buf), 1));
 
   close(sockets[0]);
 }
diff --git a/otautil/include/otautil/rangeset.h b/otautil/include/otautil/rangeset.h
index c4234d1..e91d02c 100644
--- a/otautil/include/otautil/rangeset.h
+++ b/otautil/include/otautil/rangeset.h
@@ -30,28 +30,43 @@
 
   explicit RangeSet(std::vector<Range>&& pairs);
 
+  // Parses the given string into a RangeSet. Returns the parsed RangeSet, or an empty RangeSet on
+  // errors.
   static RangeSet Parse(const std::string& range_text);
 
+  // Appends the given Range to the current RangeSet.
+  bool PushBack(Range range);
+
+  // Clears all the ranges from the RangeSet.
+  void Clear();
+
   std::string ToString() const;
 
-  // Get the block number for the i-th (starting from 0) block in the RangeSet.
+  // Gets the block number for the i-th (starting from 0) block in the RangeSet.
   size_t GetBlockNumber(size_t idx) const;
 
-  // RangeSet has half-closed half-open bounds. For example, "3,5" contains blocks 3 and 4. So "3,5"
-  // and "5,7" are not overlapped.
+  // Returns whether the current RangeSet overlaps with other. RangeSet has half-closed half-open
+  // bounds. For example, "3,5" contains blocks 3 and 4. So "3,5" and "5,7" are not overlapped.
   bool Overlaps(const RangeSet& other) const;
 
-  // size() gives the number of Range's in this RangeSet.
+  // Returns a vector of RangeSets that contain the same set of blocks represented by the current
+  // RangeSet. The RangeSets in the vector contain similar number of blocks, with a maximum delta
+  // of 1-block between any two of them. For example, 14 blocks would be split into 4 + 4 + 3 + 3,
+  // as opposed to 4 + 4 + 4 + 2. If the total number of blocks (T) is less than groups, it
+  // returns a vector of T 1-block RangeSets. Otherwise the number of the returned RangeSets must
+  // equal to groups. The current RangeSet remains intact after the split.
+  std::vector<RangeSet> Split(size_t groups) const;
+
+  // Returns the number of Range's in this RangeSet.
   size_t size() const {
     return ranges_.size();
   }
 
-  // blocks() gives the number of all blocks in this RangeSet.
+  // Returns the total number of blocks in this RangeSet.
   size_t blocks() const {
     return blocks_;
   }
 
-  // We provide const iterators only.
   std::vector<Range>::const_iterator cbegin() const {
     return ranges_.cbegin();
   }
@@ -60,13 +75,20 @@
     return ranges_.cend();
   }
 
-  // Need to provide begin()/end() since range-based loop expects begin()/end().
+  std::vector<Range>::iterator begin() {
+    return ranges_.begin();
+  }
+
+  std::vector<Range>::iterator end() {
+    return ranges_.end();
+  }
+
   std::vector<Range>::const_iterator begin() const {
-    return ranges_.cbegin();
+    return ranges_.begin();
   }
 
   std::vector<Range>::const_iterator end() const {
-    return ranges_.cend();
+    return ranges_.end();
   }
 
   // Reverse const iterators for MoveRange().
@@ -78,6 +100,11 @@
     return ranges_.crend();
   }
 
+  // Returns whether the RangeSet is valid (i.e. non-empty).
+  explicit operator bool() const {
+    return !ranges_.empty();
+  }
+
   const Range& operator[](size_t i) const {
     return ranges_[i];
   }
@@ -109,6 +136,9 @@
 // every block in the original source.
 class SortedRangeSet : public RangeSet {
  public:
+  // The block size when working with offset and file length.
+  static constexpr size_t kBlockSize = 4096;
+
   SortedRangeSet() {}
 
   // Ranges in the the set should be mutually exclusive; and they're sorted by the start block.
@@ -122,8 +152,6 @@
   // Compute the block range the file occupies, and insert that range.
   void Insert(size_t start, size_t len);
 
-  void Clear();
-
   using RangeSet::Overlaps;
 
   bool Overlaps(size_t start, size_t len) const;
diff --git a/otautil/rangeset.cpp b/otautil/rangeset.cpp
index a121a4e..96955b9 100644
--- a/otautil/rangeset.cpp
+++ b/otautil/rangeset.cpp
@@ -16,8 +16,10 @@
 
 #include "otautil/rangeset.h"
 
+#include <limits.h>
 #include <stddef.h>
 
+#include <algorithm>
 #include <string>
 #include <utility>
 #include <vector>
@@ -28,47 +30,119 @@
 #include <android-base/strings.h>
 
 RangeSet::RangeSet(std::vector<Range>&& pairs) {
-  CHECK_NE(pairs.size(), static_cast<size_t>(0)) << "Invalid number of tokens";
-
-  // Sanity check the input.
-  size_t result = 0;
-  for (const auto& range : pairs) {
-    CHECK_LT(range.first, range.second) << "Empty or negative range: " << range.first << ", "
-                                        << range.second;
-    size_t sz = range.second - range.first;
-    CHECK_LE(result, SIZE_MAX - sz) << "RangeSet size overflow";
-    result += sz;
+  blocks_ = 0;
+  if (pairs.empty()) {
+    LOG(ERROR) << "Invalid number of tokens";
+    return;
   }
 
-  ranges_ = pairs;
-  blocks_ = result;
+  for (const auto& range : pairs) {
+    if (!PushBack(range)) {
+      Clear();
+      return;
+    }
+  }
 }
 
 RangeSet RangeSet::Parse(const std::string& range_text) {
   std::vector<std::string> pieces = android::base::Split(range_text, ",");
-  CHECK_GE(pieces.size(), static_cast<size_t>(3)) << "Invalid range text: " << range_text;
+  if (pieces.size() < 3) {
+    LOG(ERROR) << "Invalid range text: " << range_text;
+    return {};
+  }
 
   size_t num;
-  CHECK(android::base::ParseUint(pieces[0], &num, static_cast<size_t>(INT_MAX)))
-      << "Failed to parse the number of tokens: " << range_text;
-
-  CHECK_NE(num, static_cast<size_t>(0)) << "Invalid number of tokens: " << range_text;
-  CHECK_EQ(num % 2, static_cast<size_t>(0)) << "Number of tokens must be even: " << range_text;
-  CHECK_EQ(num, pieces.size() - 1) << "Mismatching number of tokens: " << range_text;
+  if (!android::base::ParseUint(pieces[0], &num, static_cast<size_t>(INT_MAX))) {
+    LOG(ERROR) << "Failed to parse the number of tokens: " << range_text;
+    return {};
+  }
+  if (num == 0) {
+    LOG(ERROR) << "Invalid number of tokens: " << range_text;
+    return {};
+  }
+  if (num % 2 != 0) {
+    LOG(ERROR) << "Number of tokens must be even: " << range_text;
+    return {};
+  }
+  if (num != pieces.size() - 1) {
+    LOG(ERROR) << "Mismatching number of tokens: " << range_text;
+    return {};
+  }
 
   std::vector<Range> pairs;
   for (size_t i = 0; i < num; i += 2) {
     size_t first;
-    CHECK(android::base::ParseUint(pieces[i + 1], &first, static_cast<size_t>(INT_MAX)));
     size_t second;
-    CHECK(android::base::ParseUint(pieces[i + 2], &second, static_cast<size_t>(INT_MAX)));
-
+    if (!android::base::ParseUint(pieces[i + 1], &first, static_cast<size_t>(INT_MAX)) ||
+        !android::base::ParseUint(pieces[i + 2], &second, static_cast<size_t>(INT_MAX))) {
+      return {};
+    }
     pairs.emplace_back(first, second);
   }
-
   return RangeSet(std::move(pairs));
 }
 
+bool RangeSet::PushBack(Range range) {
+  if (range.first >= range.second) {
+    LOG(ERROR) << "Empty or negative range: " << range.first << ", " << range.second;
+    return false;
+  }
+  size_t sz = range.second - range.first;
+  if (blocks_ >= SIZE_MAX - sz) {
+    LOG(ERROR) << "RangeSet size overflow";
+    return false;
+  }
+
+  ranges_.push_back(std::move(range));
+  blocks_ += sz;
+  return true;
+}
+
+void RangeSet::Clear() {
+  ranges_.clear();
+  blocks_ = 0;
+}
+
+std::vector<RangeSet> RangeSet::Split(size_t groups) const {
+  if (ranges_.empty() || groups == 0) return {};
+
+  if (blocks_ < groups) {
+    groups = blocks_;
+  }
+
+  // Evenly distribute blocks, with the first few groups possibly containing one more.
+  size_t mean = blocks_ / groups;
+  std::vector<size_t> blocks_per_group(groups, mean);
+  std::fill_n(blocks_per_group.begin(), blocks_ % groups, mean + 1);
+
+  std::vector<RangeSet> result;
+
+  // Forward iterate Ranges and fill up each group with the desired number of blocks.
+  auto it = ranges_.cbegin();
+  Range range = *it;
+  for (const auto& blocks : blocks_per_group) {
+    RangeSet buffer;
+    size_t needed = blocks;
+    while (needed > 0) {
+      size_t range_blocks = range.second - range.first;
+      if (range_blocks > needed) {
+        // Split the current range and don't advance the iterator.
+        buffer.PushBack({ range.first, range.first + needed });
+        range.first = range.first + needed;
+        break;
+      }
+      buffer.PushBack(range);
+      it++;
+      if (it != ranges_.cend()) {
+        range = *it;
+      }
+      needed -= range_blocks;
+    }
+    result.push_back(std::move(buffer));
+  }
+  return result;
+}
+
 std::string RangeSet::ToString() const {
   if (ranges_.empty()) {
     return "";
@@ -114,8 +188,6 @@
   return false;
 }
 
-static constexpr size_t kBlockSize = 4096;
-
 // Ranges in the the set should be mutually exclusive; and they're sorted by the start block.
 SortedRangeSet::SortedRangeSet(std::vector<Range>&& pairs) : RangeSet(std::move(pairs)) {
   std::sort(ranges_.begin(), ranges_.end());
@@ -158,11 +230,6 @@
   Insert(to_insert);
 }
 
-void SortedRangeSet::Clear() {
-  blocks_ = 0;
-  ranges_.clear();
-}
-
 bool SortedRangeSet::Overlaps(size_t start, size_t len) const {
   RangeSet rs({ { start / kBlockSize, (start + len - 1) / kBlockSize + 1 } });
   return Overlaps(rs);
diff --git a/roots.cpp b/roots.cpp
index c0348d7..3cc7b41 100644
--- a/roots.cpp
+++ b/roots.cpp
@@ -321,7 +321,9 @@
   }
 
   // Has to be f2fs because we checked earlier.
-  std::vector<std::string> f2fs_args = { "/sbin/mkfs.f2fs", "-t", "-d1", v->blk_device };
+  std::vector<std::string> f2fs_args = { "/sbin/mkfs.f2fs", "-d1", "-f",
+                                         "-O", "encrypt", "-O", "quota",
+                                         v->blk_device };
   if (length >= 512) {
     f2fs_args.push_back(std::to_string(length / 512));
   }
diff --git a/tests/Android.mk b/tests/Android.mk
index 8ebb603..d911c25 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -30,7 +30,8 @@
     libutils \
     libz \
     libselinux \
-    libbase
+    libbase \
+    libBionicGtestMain
 
 LOCAL_SRC_FILES := \
     unit/asn1_decoder_test.cpp \
@@ -50,7 +51,8 @@
 LOCAL_MODULE := recovery_manual_test
 LOCAL_STATIC_LIBRARIES := \
     libminui \
-    libbase
+    libbase \
+    libBionicGtestMain
 
 LOCAL_SRC_FILES := manual/recovery_test.cpp
 LOCAL_SHARED_LIBRARIES := \
@@ -163,6 +165,7 @@
     libsquashfs_utils \
     libcutils \
     libbrotli \
+    libBionicGtestMain \
     $(tune2fs_static_libraries)
 
 testdata_files := $(call find-subdir-files, testdata/*)
@@ -212,7 +215,8 @@
     libbz \
     libdivsufsort64 \
     libdivsufsort \
-    libz
+    libz \
+    libBionicGtestMain
 LOCAL_SHARED_LIBRARIES := \
     liblog
 include $(BUILD_HOST_NATIVE_TEST)
diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp
index 15ec08f..21c9a52 100644
--- a/tests/component/applypatch_test.cpp
+++ b/tests/component/applypatch_test.cpp
@@ -53,8 +53,7 @@
 }
 
 static void mangle_file(const std::string& fname) {
-  std::string content;
-  content.reserve(1024);
+  std::string content(1024, '\0');
   for (size_t i = 0; i < 1024; i++) {
     content[i] = rand() % 256;
   }
@@ -63,16 +62,11 @@
 
 class ApplyPatchTest : public ::testing::Test {
  public:
-  static void SetUpTestCase() {
+  virtual void SetUp() override {
     // set up files
     old_file = from_testdata_base("old.file");
     new_file = from_testdata_base("new.file");
-    patch_file = from_testdata_base("patch.bsdiff");
-    rand_file = "/cache/applypatch_test_rand.file";
-    cache_file = "/cache/saved.file";
-
-    // write stuff to rand_file
-    ASSERT_TRUE(android::base::WriteStringToFile("hello", rand_file));
+    nonexistent_file = from_testdata_base("nonexistent.file");
 
     // set up SHA constants
     sha1sum(old_file, &old_sha1, &old_size);
@@ -82,56 +76,35 @@
     bad_sha1_b = android::base::StringPrintf("%040x", rand());
   }
 
-  static std::string old_file;
-  static std::string new_file;
-  static std::string rand_file;
-  static std::string cache_file;
-  static std::string patch_file;
+  std::string old_file;
+  std::string new_file;
+  std::string nonexistent_file;
 
-  static std::string old_sha1;
-  static std::string new_sha1;
-  static std::string bad_sha1_a;
-  static std::string bad_sha1_b;
+  std::string old_sha1;
+  std::string new_sha1;
+  std::string bad_sha1_a;
+  std::string bad_sha1_b;
 
-  static size_t old_size;
-  static size_t new_size;
+  size_t old_size;
+  size_t new_size;
 };
 
-static void cp(const std::string& src, const std::string& tgt) {
-  std::string cmd = "cp " + src + " " + tgt;
-  system(cmd.c_str());
-}
-
-static void backup_old() {
-  cp(ApplyPatchTest::old_file, ApplyPatchTest::cache_file);
-}
-
-static void restore_old() {
-  cp(ApplyPatchTest::cache_file, ApplyPatchTest::old_file);
-}
-
 class ApplyPatchCacheTest : public ApplyPatchTest {
- public:
-  virtual void SetUp() {
-    backup_old();
-  }
-
-  virtual void TearDown() {
-    restore_old();
+ protected:
+  void SetUp() override {
+    ApplyPatchTest::SetUp();
+    cache_temp_source = old_file;
   }
 };
 
-std::string ApplyPatchTest::old_file;
-std::string ApplyPatchTest::new_file;
-std::string ApplyPatchTest::rand_file;
-std::string ApplyPatchTest::patch_file;
-std::string ApplyPatchTest::cache_file;
-std::string ApplyPatchTest::old_sha1;
-std::string ApplyPatchTest::new_sha1;
-std::string ApplyPatchTest::bad_sha1_a;
-std::string ApplyPatchTest::bad_sha1_b;
-size_t ApplyPatchTest::old_size;
-size_t ApplyPatchTest::new_size;
+class ApplyPatchModesTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    cache_temp_source = cache_source.path;
+  }
+
+  TemporaryFile cache_source;
+};
 
 TEST_F(ApplyPatchTest, CheckModeSkip) {
   std::vector<std::string> sha1s;
@@ -189,43 +162,31 @@
   ASSERT_EQ(0, applypatch_check(src_file.c_str(), sha1s));
 }
 
-TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSingle) {
-  mangle_file(old_file);
-  std::vector<std::string> sha1s = { old_sha1 };
-  ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
+TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSourceSingle) {
+  TemporaryFile temp_file;
+  mangle_file(temp_file.path);
+  std::vector<std::string> sha1s_single = { old_sha1 };
+  ASSERT_EQ(0, applypatch_check(temp_file.path, sha1s_single));
+  ASSERT_EQ(0, applypatch_check(nonexistent_file.c_str(), sha1s_single));
 }
 
-TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedMultiple) {
-  mangle_file(old_file);
-  std::vector<std::string> sha1s = { bad_sha1_a, old_sha1, bad_sha1_b };
-  ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
+TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSourceMultiple) {
+  TemporaryFile temp_file;
+  mangle_file(temp_file.path);
+  std::vector<std::string> sha1s_multiple = { bad_sha1_a, old_sha1, bad_sha1_b };
+  ASSERT_EQ(0, applypatch_check(temp_file.path, sha1s_multiple));
+  ASSERT_EQ(0, applypatch_check(nonexistent_file.c_str(), sha1s_multiple));
 }
 
-TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedFailure) {
-  mangle_file(old_file);
-  std::vector<std::string> sha1s = { bad_sha1_a, bad_sha1_b };
-  ASSERT_NE(0, applypatch_check(&old_file[0], sha1s));
+TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSourceFailure) {
+  TemporaryFile temp_file;
+  mangle_file(temp_file.path);
+  std::vector<std::string> sha1s_failure = { bad_sha1_a, bad_sha1_b };
+  ASSERT_NE(0, applypatch_check(temp_file.path, sha1s_failure));
+  ASSERT_NE(0, applypatch_check(nonexistent_file.c_str(), sha1s_failure));
 }
 
-TEST_F(ApplyPatchCacheTest, CheckCacheMissingSingle) {
-  unlink(&old_file[0]);
-  std::vector<std::string> sha1s = { old_sha1 };
-  ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
-}
-
-TEST_F(ApplyPatchCacheTest, CheckCacheMissingMultiple) {
-  unlink(&old_file[0]);
-  std::vector<std::string> sha1s = { bad_sha1_a, old_sha1, bad_sha1_b };
-  ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
-}
-
-TEST_F(ApplyPatchCacheTest, CheckCacheMissingFailure) {
-  unlink(&old_file[0]);
-  std::vector<std::string> sha1s = { bad_sha1_a, bad_sha1_b };
-  ASSERT_NE(0, applypatch_check(&old_file[0], sha1s));
-}
-
-TEST(ApplyPatchModesTest, InvalidArgs) {
+TEST_F(ApplyPatchModesTest, InvalidArgs) {
   // At least two args (including the filename).
   ASSERT_EQ(2, applypatch_modes(1, (const char* []){ "applypatch" }));
 
@@ -233,7 +194,7 @@
   ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-x" }));
 }
 
-TEST(ApplyPatchModesTest, PatchModeEmmcTarget) {
+TEST_F(ApplyPatchModesTest, PatchModeEmmcTarget) {
   std::string boot_img = from_testdata_base("boot.img");
   size_t boot_img_size;
   std::string boot_img_sha1;
@@ -303,7 +264,7 @@
   ASSERT_EQ(0, applypatch_modes(args3.size(), args3.data()));
 }
 
-TEST(ApplyPatchModesTest, PatchModeInvalidArgs) {
+TEST_F(ApplyPatchModesTest, PatchModeInvalidArgs) {
   // Invalid bonus file.
   ASSERT_NE(0, applypatch_modes(3, (const char* []){ "applypatch", "-b", "/doesntexist" }));
 
@@ -364,11 +325,11 @@
   ASSERT_NE(0, applypatch_modes(args6.size(), args6.data()));
 }
 
-TEST(ApplyPatchModesTest, CheckModeInvalidArgs) {
+TEST_F(ApplyPatchModesTest, CheckModeInvalidArgs) {
   // Insufficient args.
   ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-c" }));
 }
 
-TEST(ApplyPatchModesTest, ShowLicenses) {
+TEST_F(ApplyPatchModesTest, ShowLicenses) {
   ASSERT_EQ(0, applypatch_modes(2, (const char* []){ "applypatch", "-l" }));
 }
diff --git a/tests/component/bootloader_message_test.cpp b/tests/component/bootloader_message_test.cpp
index b38bc71..6cc59a4 100644
--- a/tests/component/bootloader_message_test.cpp
+++ b/tests/component/bootloader_message_test.cpp
@@ -18,53 +18,12 @@
 #include <vector>
 
 #include <android-base/strings.h>
+#include <android-base/test_utils.h>
 #include <bootloader_message/bootloader_message.h>
 #include <gtest/gtest.h>
 
-class BootloaderMessageTest : public ::testing::Test {
- protected:
-  BootloaderMessageTest() : has_misc(true) {}
-
-  virtual void SetUp() override {
-    std::string err;
-    has_misc = !get_bootloader_message_blk_device(&err).empty();
-  }
-
-  virtual void TearDown() override {
-    // Clear the BCB.
-    if (has_misc) {
-      std::string err;
-      ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err;
-    }
-  }
-
-  bool has_misc;
-};
-
-TEST_F(BootloaderMessageTest, clear_bootloader_message) {
-  if (!has_misc) {
-    GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
-    return;
-  }
-
-  // Clear the BCB.
-  std::string err;
-  ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err;
-
-  // Verify the content.
-  bootloader_message boot;
-  ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
-
-  // All the bytes should be cleared.
-  ASSERT_EQ(std::string(sizeof(boot), '\0'),
-            std::string(reinterpret_cast<const char*>(&boot), sizeof(boot)));
-}
-
-TEST_F(BootloaderMessageTest, read_and_write_bootloader_message) {
-  if (!has_misc) {
-    GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
-    return;
-  }
+TEST(BootloaderMessageTest, read_and_write_bootloader_message) {
+  TemporaryFile temp_misc;
 
   // Write the BCB.
   bootloader_message boot = {};
@@ -73,90 +32,71 @@
   strlcpy(boot.status, "status1", sizeof(boot.status));
 
   std::string err;
-  ASSERT_TRUE(write_bootloader_message(boot, &err)) << "Failed to write BCB: " << err;
+  ASSERT_TRUE(write_bootloader_message_to(boot, temp_misc.path, &err))
+      << "Failed to write BCB: " << err;
 
   // Read and verify.
   bootloader_message boot_verify;
-  ASSERT_TRUE(read_bootloader_message(&boot_verify, &err)) << "Failed to read BCB: " << err;
+  ASSERT_TRUE(read_bootloader_message_from(&boot_verify, temp_misc.path, &err))
+      << "Failed to read BCB: " << err;
 
   ASSERT_EQ(std::string(reinterpret_cast<const char*>(&boot), sizeof(boot)),
             std::string(reinterpret_cast<const char*>(&boot_verify), sizeof(boot_verify)));
 }
 
-TEST_F(BootloaderMessageTest, write_bootloader_message_options) {
-  if (!has_misc) {
-    GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
-    return;
-  }
-
+TEST(BootloaderMessageTest, update_bootloader_message_in_struct) {
   // Write the options to BCB.
   std::vector<std::string> options = { "option1", "option2" };
-  std::string err;
-  ASSERT_TRUE(write_bootloader_message(options, &err)) << "Failed to write BCB: " << err;
 
-  // Inject some bytes into boot, which should be overwritten while reading.
-  bootloader_message boot;
+  bootloader_message boot = {};
+  // Inject some bytes into boot.
   strlcpy(boot.recovery, "random message", sizeof(boot.recovery));
+  strlcpy(boot.status, "status bytes", sizeof(boot.status));
+  strlcpy(boot.stage, "stage bytes", sizeof(boot.stage));
   strlcpy(boot.reserved, "reserved bytes", sizeof(boot.reserved));
 
-  ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
+  ASSERT_TRUE(update_bootloader_message_in_struct(&boot, options));
 
   // Verify that command and recovery fields should be set.
   ASSERT_EQ("boot-recovery", std::string(boot.command));
   std::string expected = "recovery\n" + android::base::Join(options, "\n") + "\n";
   ASSERT_EQ(expected, std::string(boot.recovery));
 
-  // The rest should be cleared.
-  ASSERT_EQ(std::string(sizeof(boot.status), '\0'), std::string(boot.status, sizeof(boot.status)));
-  ASSERT_EQ(std::string(sizeof(boot.stage), '\0'), std::string(boot.stage, sizeof(boot.stage)));
-  ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'),
-            std::string(boot.reserved, sizeof(boot.reserved)));
+  // The rest should be intact.
+  ASSERT_EQ("status bytes", std::string(boot.status));
+  ASSERT_EQ("stage bytes", std::string(boot.stage));
+  ASSERT_EQ("reserved bytes", std::string(boot.reserved));
 }
 
-TEST_F(BootloaderMessageTest, write_bootloader_message_options_empty) {
-  if (!has_misc) {
-    GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
-    return;
-  }
-
+TEST(BootloaderMessageTest, update_bootloader_message_recovery_options_empty) {
   // Write empty vector.
   std::vector<std::string> options;
-  std::string err;
-  ASSERT_TRUE(write_bootloader_message(options, &err)) << "Failed to write BCB: " << err;
 
   // Read and verify.
-  bootloader_message boot;
-  ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
+  bootloader_message boot = {};
+  ASSERT_TRUE(update_bootloader_message_in_struct(&boot, options));
 
   // command and recovery fields should be set.
   ASSERT_EQ("boot-recovery", std::string(boot.command));
   ASSERT_EQ("recovery\n", std::string(boot.recovery));
 
-  // The rest should be cleared.
+  // The rest should be empty.
   ASSERT_EQ(std::string(sizeof(boot.status), '\0'), std::string(boot.status, sizeof(boot.status)));
   ASSERT_EQ(std::string(sizeof(boot.stage), '\0'), std::string(boot.stage, sizeof(boot.stage)));
   ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'),
             std::string(boot.reserved, sizeof(boot.reserved)));
 }
 
-TEST_F(BootloaderMessageTest, write_bootloader_message_options_long) {
-  if (!has_misc) {
-    GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
-    return;
-  }
-
+TEST(BootloaderMessageTest, update_bootloader_message_recovery_options_long) {
   // Write super long message.
   std::vector<std::string> options;
   for (int i = 0; i < 100; i++) {
     options.push_back("option: " + std::to_string(i));
   }
 
-  std::string err;
-  ASSERT_TRUE(write_bootloader_message(options, &err)) << "Failed to write BCB: " << err;
-
   // Read and verify.
-  bootloader_message boot;
-  ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
+  bootloader_message boot = {};
+  ASSERT_TRUE(update_bootloader_message_in_struct(&boot, options));
 
   // Make sure it's long enough.
   std::string expected = "recovery\n" + android::base::Join(options, "\n") + "\n";
@@ -167,40 +107,10 @@
   ASSERT_EQ(expected.substr(0, sizeof(boot.recovery) - 1), std::string(boot.recovery));
   ASSERT_EQ('\0', boot.recovery[sizeof(boot.recovery) - 1]);
 
-  // The rest should be cleared.
+  // The rest should be empty.
   ASSERT_EQ(std::string(sizeof(boot.status), '\0'), std::string(boot.status, sizeof(boot.status)));
   ASSERT_EQ(std::string(sizeof(boot.stage), '\0'), std::string(boot.stage, sizeof(boot.stage)));
   ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'),
             std::string(boot.reserved, sizeof(boot.reserved)));
 }
 
-TEST_F(BootloaderMessageTest, update_bootloader_message) {
-  if (!has_misc) {
-    GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
-    return;
-  }
-
-  // Inject some bytes into boot, which should be not overwritten later.
-  bootloader_message boot;
-  strlcpy(boot.recovery, "random message", sizeof(boot.recovery));
-  strlcpy(boot.reserved, "reserved bytes", sizeof(boot.reserved));
-  std::string err;
-  ASSERT_TRUE(write_bootloader_message(boot, &err)) << "Failed to write BCB: " << err;
-
-  // Update the BCB message.
-  std::vector<std::string> options = { "option1", "option2" };
-  ASSERT_TRUE(update_bootloader_message(options, &err)) << "Failed to update BCB: " << err;
-
-  bootloader_message boot_verify;
-  ASSERT_TRUE(read_bootloader_message(&boot_verify, &err)) << "Failed to read BCB: " << err;
-
-  // Verify that command and recovery fields should be set.
-  ASSERT_EQ("boot-recovery", std::string(boot_verify.command));
-  std::string expected = "recovery\n" + android::base::Join(options, "\n") + "\n";
-  ASSERT_EQ(expected, std::string(boot_verify.recovery));
-
-  // The rest should be intact.
-  ASSERT_EQ(std::string(boot.status), std::string(boot_verify.status));
-  ASSERT_EQ(std::string(boot.stage), std::string(boot_verify.stage));
-  ASSERT_EQ(std::string(boot.reserved), std::string(boot_verify.reserved));
-}
diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp
index 6de804e..728b6cc 100644
--- a/tests/component/imgdiff_test.cpp
+++ b/tests/component/imgdiff_test.cpp
@@ -657,19 +657,23 @@
   }
 }
 
-// Look for the generated source and patch pieces in the debug_dir and generate the target on
-// each pair. Concatenate the split target and match against the orignal one.
+// Look for the source and patch pieces in debug_dir. Generate a target piece from each pair.
+// Concatenate all the target pieces and match against the orignal one. Used pieces in debug_dir
+// will be cleaned up.
 static void GenerateAndCheckSplitTarget(const std::string& debug_dir, size_t count,
                                         const std::string& tgt) {
   std::string patched;
   for (size_t i = 0; i < count; i++) {
     std::string split_src_path = android::base::StringPrintf("%s/src-%zu", debug_dir.c_str(), i);
-    std::string split_patch_path = android::base::StringPrintf("%s/patch-%zu", debug_dir.c_str(), i);
-
     std::string split_src;
-    std::string split_patch;
     ASSERT_TRUE(android::base::ReadFileToString(split_src_path, &split_src));
+    ASSERT_EQ(0, unlink(split_src_path.c_str()));
+
+    std::string split_patch_path =
+        android::base::StringPrintf("%s/patch-%zu", debug_dir.c_str(), i);
+    std::string split_patch;
     ASSERT_TRUE(android::base::ReadFileToString(split_patch_path, &split_patch));
+    ASSERT_EQ(0, unlink(split_patch_path.c_str()));
 
     std::string split_tgt;
     GenerateTarget(split_src, split_patch, &split_tgt);
diff --git a/tests/component/sideload_test.cpp b/tests/component/sideload_test.cpp
index 40cfc69..b7109fc 100644
--- a/tests/component/sideload_test.cpp
+++ b/tests/component/sideload_test.cpp
@@ -16,6 +16,12 @@
 
 #include <unistd.h>
 
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
 #include "fuse_sideload.h"
@@ -26,11 +32,67 @@
 
 TEST(SideloadTest, run_fuse_sideload_wrong_parameters) {
   provider_vtab vtab;
-  vtab.close = [](void*) {};
+  vtab.close = [](void) {};
 
-  ASSERT_EQ(-1, run_fuse_sideload(&vtab, nullptr, 4096, 4095));
-  ASSERT_EQ(-1, run_fuse_sideload(&vtab, nullptr, 4096, (1 << 22) + 1));
+  ASSERT_EQ(-1, run_fuse_sideload(vtab, 4096, 4095));
+  ASSERT_EQ(-1, run_fuse_sideload(vtab, 4096, (1 << 22) + 1));
 
   // Too many blocks.
-  ASSERT_EQ(-1, run_fuse_sideload(&vtab, nullptr, ((1 << 18) + 1) * 4096, 4096));
+  ASSERT_EQ(-1, run_fuse_sideload(vtab, ((1 << 18) + 1) * 4096, 4096));
+}
+
+TEST(SideloadTest, run_fuse_sideload) {
+  const std::vector<std::string> blocks = {
+    std::string(2048, 'a') + std::string(2048, 'b'),
+    std::string(2048, 'c') + std::string(2048, 'd'),
+    std::string(2048, 'e') + std::string(2048, 'f'),
+    std::string(2048, 'g') + std::string(2048, 'h'),
+  };
+  const std::string content = android::base::Join(blocks, "");
+  ASSERT_EQ(16384U, content.size());
+
+  provider_vtab vtab;
+  vtab.close = [](void) {};
+  vtab.read_block = [&blocks](uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
+    if (block >= 4) return -1;
+    blocks[block].copy(reinterpret_cast<char*>(buffer), fetch_size);
+    return 0;
+  };
+
+  TemporaryDir mount_point;
+  pid_t pid = fork();
+  if (pid == 0) {
+    ASSERT_EQ(0, run_fuse_sideload(vtab, 16384, 4096, mount_point.path));
+    _exit(EXIT_SUCCESS);
+  }
+
+  std::string package = std::string(mount_point.path) + "/" + FUSE_SIDELOAD_HOST_FILENAME;
+  int status;
+  static constexpr int kSideloadInstallTimeout = 10;
+  for (int i = 0; i < kSideloadInstallTimeout; ++i) {
+    ASSERT_NE(-1, waitpid(pid, &status, WNOHANG));
+
+    struct stat sb;
+    if (stat(package.c_str(), &sb) == 0) {
+      break;
+    }
+
+    if (errno == ENOENT && i < kSideloadInstallTimeout - 1) {
+      sleep(1);
+      continue;
+    }
+    FAIL() << "Timed out waiting for the fuse-provided package.";
+  }
+
+  std::string content_via_fuse;
+  ASSERT_TRUE(android::base::ReadFileToString(package, &content_via_fuse));
+  ASSERT_EQ(content, content_via_fuse);
+
+  std::string exit_flag = std::string(mount_point.path) + "/" + FUSE_SIDELOAD_HOST_EXIT_FLAG;
+  struct stat sb;
+  ASSERT_EQ(0, stat(exit_flag.c_str(), &sb));
+
+  waitpid(pid, &status, 0);
+  ASSERT_EQ(0, WEXITSTATUS(status));
+  ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
 }
diff --git a/tests/component/uncrypt_test.cpp b/tests/component/uncrypt_test.cpp
index 3925236..55baca2 100644
--- a/tests/component/uncrypt_test.cpp
+++ b/tests/component/uncrypt_test.cpp
@@ -20,6 +20,7 @@
 #include <sys/un.h>
 #include <unistd.h>
 
+#include <algorithm>
 #include <string>
 
 #include <android-base/file.h>
@@ -38,43 +39,49 @@
 static const std::string INIT_SVC_UNCRYPT = "init.svc.uncrypt";
 static constexpr int SOCKET_CONNECTION_MAX_RETRY = 30;
 
+static void StopService() {
+  ASSERT_TRUE(android::base::SetProperty("ctl.stop", "setup-bcb"));
+  ASSERT_TRUE(android::base::SetProperty("ctl.stop", "clear-bcb"));
+  ASSERT_TRUE(android::base::SetProperty("ctl.stop", "uncrypt"));
+
+  bool success = false;
+  for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
+    std::string setup_bcb = android::base::GetProperty(INIT_SVC_SETUP_BCB, "");
+    std::string clear_bcb = android::base::GetProperty(INIT_SVC_CLEAR_BCB, "");
+    std::string uncrypt = android::base::GetProperty(INIT_SVC_UNCRYPT, "");
+    GTEST_LOG_(INFO) << "setup-bcb: [" << setup_bcb << "] clear-bcb: [" << clear_bcb
+                     << "] uncrypt: [" << uncrypt << "]";
+    if (setup_bcb != "running" && clear_bcb != "running" && uncrypt != "running") {
+      success = true;
+      break;
+    }
+    sleep(1);
+  }
+
+  ASSERT_TRUE(success) << "uncrypt service is not available.";
+}
+
 class UncryptTest : public ::testing::Test {
  protected:
   UncryptTest() : has_misc(true) {}
 
-  virtual void SetUp() override {
-    ASSERT_TRUE(android::base::SetProperty("ctl.stop", "setup-bcb"));
-    ASSERT_TRUE(android::base::SetProperty("ctl.stop", "clear-bcb"));
-    ASSERT_TRUE(android::base::SetProperty("ctl.stop", "uncrypt"));
-
-    bool success = false;
-    for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
-      std::string setup_bcb = android::base::GetProperty(INIT_SVC_SETUP_BCB, "");
-      std::string clear_bcb = android::base::GetProperty(INIT_SVC_CLEAR_BCB, "");
-      std::string uncrypt = android::base::GetProperty(INIT_SVC_UNCRYPT, "");
-      LOG(INFO) << "setup-bcb: [" << setup_bcb << "] clear-bcb: [" << clear_bcb << "] uncrypt: ["
-                << uncrypt << "]";
-      if (setup_bcb != "running" && clear_bcb != "running" && uncrypt != "running") {
-        success = true;
-        break;
-      }
-      sleep(1);
-    }
-
-    ASSERT_TRUE(success) << "uncrypt service is not available.";
-
+  void SetUp() override {
     std::string err;
     has_misc = !get_bootloader_message_blk_device(&err).empty();
   }
 
+  void TearDown() override {
+    // Clear the BCB.
+    if (has_misc) {
+      std::string err;
+      ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err;
+    }
+  }
+
   void SetupOrClearBcb(bool isSetup, const std::string& message,
                        const std::string& message_in_bcb) const {
-    if (!has_misc) {
-      GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
-      return;
-    }
-
-    // Trigger the setup-bcb service.
+    // Restart the setup-bcb service.
+    StopService();
     ASSERT_TRUE(android::base::SetProperty("ctl.start", isSetup ? "setup-bcb" : "clear-bcb"));
 
     // Test tends to be flaky if proceeding immediately ("Transport endpoint is not connected").
@@ -144,27 +151,49 @@
     }
   }
 
+  void VerifyBootloaderMessage(const std::string& expected) {
+    std::string err;
+    bootloader_message boot;
+    ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
+
+    // Check that we have all the expected bytes.
+    ASSERT_EQ(expected, std::string(reinterpret_cast<const char*>(&boot), sizeof(boot)));
+  }
+
   bool has_misc;
 };
 
 TEST_F(UncryptTest, setup_bcb) {
+  if (!has_misc) {
+    GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
+    return;
+  }
+
+  std::string random_data;
+  random_data.reserve(sizeof(bootloader_message));
+  generate_n(back_inserter(random_data), sizeof(bootloader_message), []() { return rand() % 128; });
+
+  bootloader_message boot;
+  memcpy(&boot, random_data.c_str(), random_data.size());
+
+  std::string err;
+  ASSERT_TRUE(write_bootloader_message(boot, &err)) << "Failed to write BCB: " << err;
+  VerifyBootloaderMessage(random_data);
+
+  ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err;
+  VerifyBootloaderMessage(std::string(sizeof(bootloader_message), '\0'));
+
   std::string message = "--update_message=abc value";
   std::string message_in_bcb = "recovery\n--update_message=abc value\n";
   SetupOrClearBcb(true, message, message_in_bcb);
-}
 
-TEST_F(UncryptTest, clear_bcb) {
   SetupOrClearBcb(false, "", "");
-}
 
-TEST_F(UncryptTest, setup_bcb_wipe_ab) {
   TemporaryFile wipe_package;
   ASSERT_TRUE(android::base::WriteStringToFile(std::string(345, 'a'), wipe_package.path));
 
   // It's expected to store a wipe package in /misc, with the package size passed to recovery.
-  std::string message =
-      "--wipe_ab\n--wipe_package="s + wipe_package.path + "\n--reason=wipePackage"s;
-  std::string message_in_bcb =
-      "recovery\n--wipe_ab\n--wipe_package_size=345\n--reason=wipePackage\n";
+  message = "--wipe_ab\n--wipe_package="s + wipe_package.path + "\n--reason=wipePackage"s;
+  message_in_bcb = "recovery\n--wipe_ab\n--wipe_package_size=345\n--reason=wipePackage\n";
   SetupOrClearBcb(true, message, message_in_bcb);
 }
diff --git a/tests/unit/rangeset_test.cpp b/tests/unit/rangeset_test.cpp
index b3ed992..7ae193e 100644
--- a/tests/unit/rangeset_test.cpp
+++ b/tests/unit/rangeset_test.cpp
@@ -17,12 +17,24 @@
 #include <signal.h>
 #include <sys/types.h>
 
+#include <limits>
 #include <vector>
 
 #include <gtest/gtest.h>
 
 #include "otautil/rangeset.h"
 
+TEST(RangeSetTest, ctor) {
+  RangeSet rs(std::vector<Range>{ Range{ 8, 10 }, Range{ 1, 5 } });
+  ASSERT_TRUE(rs);
+
+  RangeSet rs2(std::vector<Range>{});
+  ASSERT_FALSE(rs2);
+
+  RangeSet rs3(std::vector<Range>{ Range{ 8, 10 }, Range{ 5, 1 } });
+  ASSERT_FALSE(rs3);
+}
+
 TEST(RangeSetTest, Parse_smoke) {
   RangeSet rs = RangeSet::Parse("2,1,10");
   ASSERT_EQ(static_cast<size_t>(1), rs.size());
@@ -37,27 +49,64 @@
 
   // Leading zeros are fine. But android::base::ParseUint() doesn't like trailing zeros like "10 ".
   ASSERT_EQ(rs, RangeSet::Parse(" 2, 1,   10"));
-  ASSERT_EXIT(RangeSet::Parse("2,1,10 "), ::testing::KilledBySignal(SIGABRT), "");
+  ASSERT_FALSE(RangeSet::Parse("2,1,10 "));
 }
 
 TEST(RangeSetTest, Parse_InvalidCases) {
   // Insufficient number of tokens.
-  ASSERT_EXIT(RangeSet::Parse(""), ::testing::KilledBySignal(SIGABRT), "");
-  ASSERT_EXIT(RangeSet::Parse("2,1"), ::testing::KilledBySignal(SIGABRT), "");
+  ASSERT_FALSE(RangeSet::Parse(""));
+  ASSERT_FALSE(RangeSet::Parse("2,1"));
 
   // The first token (i.e. the number of following tokens) is invalid.
-  ASSERT_EXIT(RangeSet::Parse("a,1,1"), ::testing::KilledBySignal(SIGABRT), "");
-  ASSERT_EXIT(RangeSet::Parse("3,1,1"), ::testing::KilledBySignal(SIGABRT), "");
-  ASSERT_EXIT(RangeSet::Parse("-3,1,1"), ::testing::KilledBySignal(SIGABRT), "");
-  ASSERT_EXIT(RangeSet::Parse("2,1,2,3"), ::testing::KilledBySignal(SIGABRT), "");
+  ASSERT_FALSE(RangeSet::Parse("a,1,1"));
+  ASSERT_FALSE(RangeSet::Parse("3,1,1"));
+  ASSERT_FALSE(RangeSet::Parse("-3,1,1"));
+  ASSERT_FALSE(RangeSet::Parse("2,1,2,3"));
 
   // Invalid tokens.
-  ASSERT_EXIT(RangeSet::Parse("2,1,10a"), ::testing::KilledBySignal(SIGABRT), "");
-  ASSERT_EXIT(RangeSet::Parse("2,,10"), ::testing::KilledBySignal(SIGABRT), "");
+  ASSERT_FALSE(RangeSet::Parse("2,1,10a"));
+  ASSERT_FALSE(RangeSet::Parse("2,,10"));
 
   // Empty or negative range.
-  ASSERT_EXIT(RangeSet::Parse("2,2,2"), ::testing::KilledBySignal(SIGABRT), "");
-  ASSERT_EXIT(RangeSet::Parse("2,2,1"), ::testing::KilledBySignal(SIGABRT), "");
+  ASSERT_FALSE(RangeSet::Parse("2,2,2"));
+  ASSERT_FALSE(RangeSet::Parse("2,2,1"));
+}
+
+TEST(RangeSetTest, Clear) {
+  RangeSet rs = RangeSet::Parse("2,1,6");
+  ASSERT_TRUE(rs);
+  rs.Clear();
+  ASSERT_FALSE(rs);
+
+  // No-op to clear an empty RangeSet.
+  rs.Clear();
+  ASSERT_FALSE(rs);
+}
+
+TEST(RangeSetTest, PushBack) {
+  RangeSet rs;
+  ASSERT_FALSE(rs);
+
+  ASSERT_TRUE(rs.PushBack({ 3, 5 }));
+  ASSERT_EQ(RangeSet::Parse("2,3,5"), rs);
+
+  ASSERT_TRUE(rs.PushBack({ 5, 15 }));
+  ASSERT_EQ(RangeSet::Parse("4,3,5,5,15"), rs);
+  ASSERT_EQ(static_cast<size_t>(2), rs.size());
+  ASSERT_EQ(static_cast<size_t>(12), rs.blocks());
+}
+
+TEST(RangeSetTest, PushBack_InvalidInput) {
+  RangeSet rs;
+  ASSERT_FALSE(rs);
+  ASSERT_FALSE(rs.PushBack({ 5, 3 }));
+  ASSERT_FALSE(rs);
+  ASSERT_FALSE(rs.PushBack({ 15, 15 }));
+  ASSERT_FALSE(rs);
+
+  ASSERT_TRUE(rs.PushBack({ 5, 15 }));
+  ASSERT_FALSE(rs.PushBack({ 5, std::numeric_limits<size_t>::max() - 2 }));
+  ASSERT_EQ(RangeSet::Parse("2,5,15"), rs);
 }
 
 TEST(RangeSetTest, Overlaps) {
@@ -74,6 +123,86 @@
   ASSERT_FALSE(RangeSet::Parse("2,5,7").Overlaps(RangeSet::Parse("2,3,5")));
 }
 
+TEST(RangeSetTest, Split) {
+  RangeSet rs1 = RangeSet::Parse("2,1,2");
+  ASSERT_TRUE(rs1);
+  ASSERT_EQ((std::vector<RangeSet>{ RangeSet::Parse("2,1,2") }), rs1.Split(1));
+
+  RangeSet rs2 = RangeSet::Parse("2,5,10");
+  ASSERT_TRUE(rs2);
+  ASSERT_EQ((std::vector<RangeSet>{ RangeSet::Parse("2,5,8"), RangeSet::Parse("2,8,10") }),
+            rs2.Split(2));
+
+  RangeSet rs3 = RangeSet::Parse("4,0,1,5,10");
+  ASSERT_TRUE(rs3);
+  ASSERT_EQ((std::vector<RangeSet>{ RangeSet::Parse("4,0,1,5,7"), RangeSet::Parse("2,7,10") }),
+            rs3.Split(2));
+
+  RangeSet rs4 = RangeSet::Parse("6,1,3,3,4,4,5");
+  ASSERT_TRUE(rs4);
+  ASSERT_EQ((std::vector<RangeSet>{ RangeSet::Parse("2,1,3"), RangeSet::Parse("2,3,4"),
+                                    RangeSet::Parse("2,4,5") }),
+            rs4.Split(3));
+
+  RangeSet rs5 = RangeSet::Parse("2,0,10");
+  ASSERT_TRUE(rs5);
+  ASSERT_EQ((std::vector<RangeSet>{ RangeSet::Parse("2,0,3"), RangeSet::Parse("2,3,6"),
+                                    RangeSet::Parse("2,6,8"), RangeSet::Parse("2,8,10") }),
+            rs5.Split(4));
+
+  RangeSet rs6 = RangeSet::Parse(
+      "20,0,268,269,271,286,447,8350,32770,33022,98306,98558,163842,164094,196609,204800,229378,"
+      "229630,294914,295166,457564");
+  ASSERT_TRUE(rs6);
+  size_t rs6_blocks = rs6.blocks();
+  auto splits = rs6.Split(4);
+  ASSERT_EQ(
+      (std::vector<RangeSet>{
+          RangeSet::Parse("12,0,268,269,271,286,447,8350,32770,33022,98306,98558,118472"),
+          RangeSet::Parse("8,118472,163842,164094,196609,204800,229378,229630,237216"),
+          RangeSet::Parse("4,237216,294914,295166,347516"), RangeSet::Parse("2,347516,457564") }),
+      splits);
+  size_t sum = 0;
+  for (const auto& element : splits) {
+    sum += element.blocks();
+  }
+  ASSERT_EQ(rs6_blocks, sum);
+}
+
+TEST(RangeSetTest, Split_EdgeCases) {
+  // Empty RangeSet.
+  RangeSet rs1;
+  ASSERT_FALSE(rs1);
+  ASSERT_EQ((std::vector<RangeSet>{}), rs1.Split(2));
+  ASSERT_FALSE(rs1);
+
+  // Zero group.
+  RangeSet rs2 = RangeSet::Parse("2,1,5");
+  ASSERT_TRUE(rs2);
+  ASSERT_EQ((std::vector<RangeSet>{}), rs2.Split(0));
+
+  // The number of blocks equals to the number of groups.
+  RangeSet rs3 = RangeSet::Parse("2,1,5");
+  ASSERT_TRUE(rs3);
+  ASSERT_EQ((std::vector<RangeSet>{ RangeSet::Parse("2,1,2"), RangeSet::Parse("2,2,3"),
+                                    RangeSet::Parse("2,3,4"), RangeSet::Parse("2,4,5") }),
+            rs3.Split(4));
+
+  // Less blocks than the number of groups.
+  RangeSet rs4 = RangeSet::Parse("2,1,5");
+  ASSERT_TRUE(rs4);
+  ASSERT_EQ((std::vector<RangeSet>{ RangeSet::Parse("2,1,2"), RangeSet::Parse("2,2,3"),
+                                    RangeSet::Parse("2,3,4"), RangeSet::Parse("2,4,5") }),
+            rs4.Split(8));
+
+  // Less blocks than the number of groups.
+  RangeSet rs5 = RangeSet::Parse("2,0,3");
+  ASSERT_TRUE(rs5);
+  ASSERT_EQ((std::vector<RangeSet>{ RangeSet::Parse("2,0,1"), RangeSet::Parse("2,1,2"),
+                                    RangeSet::Parse("2,2,3") }),
+            rs5.Split(4));
+}
+
 TEST(RangeSetTest, GetBlockNumber) {
   RangeSet rs = RangeSet::Parse("2,1,10");
   ASSERT_EQ(static_cast<size_t>(1), rs.GetBlockNumber(0));
@@ -90,7 +219,7 @@
   ASSERT_NE(RangeSet::Parse("2,1,6"), RangeSet::Parse("2,1,7"));
   ASSERT_NE(RangeSet::Parse("2,1,6"), RangeSet::Parse("2,2,7"));
 
-  // The orders of Range's matter. "4,1,5,8,10" != "4,8,10,1,5".
+  // The orders of Range's matter, e.g. "4,1,5,8,10" != "4,8,10,1,5".
   ASSERT_NE(RangeSet::Parse("4,1,5,8,10"), RangeSet::Parse("4,8,10,1,5"));
 }
 
@@ -111,13 +240,14 @@
   ASSERT_EQ((std::vector<Range>{ Range{ 8, 10 }, Range{ 1, 5 } }), ranges);
 }
 
-TEST(RangeSetTest, tostring) {
+TEST(RangeSetTest, ToString) {
+  ASSERT_EQ("", RangeSet::Parse("").ToString());
   ASSERT_EQ("2,1,6", RangeSet::Parse("2,1,6").ToString());
   ASSERT_EQ("4,1,5,8,10", RangeSet::Parse("4,1,5,8,10").ToString());
   ASSERT_EQ("6,1,3,4,6,15,22", RangeSet::Parse("6,1,3,4,6,15,22").ToString());
 }
 
-TEST(SortedRangeSetTest, insertion) {
+TEST(SortedRangeSetTest, Insert) {
   SortedRangeSet rs({ { 2, 3 }, { 4, 6 }, { 8, 14 } });
   rs.Insert({ 1, 2 });
   ASSERT_EQ(SortedRangeSet({ { 1, 3 }, { 4, 6 }, { 8, 14 } }), rs);
diff --git a/uncrypt/Android.bp b/uncrypt/Android.bp
new file mode 100644
index 0000000..aa56d2f
--- /dev/null
+++ b/uncrypt/Android.bp
@@ -0,0 +1,39 @@
+// 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.
+
+cc_binary {
+    name: "uncrypt",
+
+    srcs: [
+        "uncrypt.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    static_libs: [
+        "libbootloader_message",
+        "libotautil",
+        "libfs_mgr",
+        "libbase",
+        "libcutils",
+        "liblog",
+    ],
+
+    init_rc: [
+        "uncrypt.rc",
+    ],
+}
diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk
deleted file mode 100644
index 601f927..0000000
--- a/uncrypt/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2014 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := uncrypt.cpp
-LOCAL_MODULE := uncrypt
-LOCAL_STATIC_LIBRARIES := \
-    libbootloader_message \
-    libotautil \
-    libbase \
-    liblog \
-    libfs_mgr \
-    libcutils
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_INIT_RC := uncrypt.rc
-
-include $(BUILD_EXECUTABLE)
diff --git a/update_verifier/Android.mk b/update_verifier/Android.mk
index 33c5fe9..0ff8854 100644
--- a/update_verifier/Android.mk
+++ b/update_verifier/Android.mk
@@ -22,6 +22,10 @@
     update_verifier.cpp
 
 LOCAL_MODULE := libupdate_verifier
+
+LOCAL_STATIC_LIBRARIES := \
+    libotautil
+
 LOCAL_SHARED_LIBRARIES := \
     libbase \
     libcutils \
@@ -54,7 +58,9 @@
 
 LOCAL_MODULE := update_verifier
 LOCAL_STATIC_LIBRARIES := \
-    libupdate_verifier
+    libupdate_verifier \
+    libotautil
+
 LOCAL_SHARED_LIBRARIES := \
     libbase \
     libcutils \
diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp
index ba7b7ae..c5e154f 100644
--- a/update_verifier/update_verifier.cpp
+++ b/update_verifier/update_verifier.cpp
@@ -58,6 +58,8 @@
 #include <android/hardware/boot/1.0/IBootControl.h>
 #include <cutils/android_reboot.h>
 
+#include "otautil/rangeset.h"
+
 using android::sp;
 using android::hardware::boot::V1_0::IBootControl;
 using android::hardware::boot::V1_0::BoolResult;
@@ -129,42 +131,33 @@
   // followed by 'count' number comma separated integers. Every two integers reprensent a
   // block range with the first number included in range but second number not included.
   // For example '4,64536,65343,74149,74150' represents: [64536,65343) and [74149,74150).
-  std::vector<std::string> ranges = android::base::Split(range_str, ",");
-  size_t range_count;
-  bool status = android::base::ParseUint(ranges[0], &range_count);
-  if (!status || (range_count == 0) || (range_count % 2 != 0) ||
-      (range_count != ranges.size() - 1)) {
-    LOG(ERROR) << "Error in parsing range string.";
+  RangeSet ranges = RangeSet::Parse(range_str);
+  if (!ranges) {
+    LOG(ERROR) << "Error parsing RangeSet string " << range_str;
     return false;
   }
-  range_count /= 2;
+
+  // RangeSet::Split() splits the ranges into multiple groups with same number of blocks (except for
+  // the last group).
+  size_t thread_num = std::thread::hardware_concurrency() ?: 4;
+  std::vector<RangeSet> groups = ranges.Split(thread_num);
 
   std::vector<std::future<bool>> threads;
-  size_t thread_num = std::thread::hardware_concurrency() ?: 4;
-  thread_num = std::min(thread_num, range_count);
-  size_t group_range_count = (range_count + thread_num - 1) / thread_num;
-
-  for (size_t t = 0; t < thread_num; t++) {
-    auto thread_func = [t, group_range_count, &dm_block_device, &ranges, &partition]() {
-      size_t blk_count = 0;
-      static constexpr size_t kBlockSize = 4096;
-      std::vector<uint8_t> buf(1024 * kBlockSize);
+  for (const auto& group : groups) {
+    auto thread_func = [&group, &dm_block_device, &partition]() {
       android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
       if (fd.get() == -1) {
         PLOG(ERROR) << "Error reading " << dm_block_device << " for partition " << partition;
         return false;
       }
 
-      for (size_t i = group_range_count * 2 * t + 1;
-           i < std::min(group_range_count * 2 * (t + 1) + 1, ranges.size()); i += 2) {
-        unsigned int range_start, range_end;
-        bool parse_status = android::base::ParseUint(ranges[i], &range_start);
-        parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end);
-        if (!parse_status || range_start >= range_end) {
-          LOG(ERROR) << "Invalid range pair " << ranges[i] << ", " << ranges[i + 1];
-          return false;
-        }
+      static constexpr size_t kBlockSize = 4096;
+      std::vector<uint8_t> buf(1024 * kBlockSize);
 
+      size_t block_count = 0;
+      for (const auto& range : group) {
+        size_t range_start = range.first;
+        size_t range_end = range.second;
         if (lseek64(fd.get(), static_cast<off64_t>(range_start) * kBlockSize, SEEK_SET) == -1) {
           PLOG(ERROR) << "lseek to " << range_start << " failed";
           return false;
@@ -179,9 +172,9 @@
           }
           remain -= to_read;
         }
-        blk_count += (range_end - range_start);
+        block_count += (range_end - range_start);
       }
-      LOG(INFO) << "Finished reading " << blk_count << " blocks on " << dm_block_device;
+      LOG(INFO) << "Finished reading " << block_count << " blocks on " << dm_block_device;
       return true;
     };
 
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index 6c7b3ef..08f9930 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -492,6 +492,10 @@
   }
 
   RangeSet src = RangeSet::Parse(params.tokens[pos++]);
+  if (!src) {
+    LOG(ERROR) << "Failed to parse range in " << params.cmdline;
+    return;
+  }
 
   RangeSet locs;
   // If there's no stashed blocks, content in the buffer is consecutive and has the same
@@ -936,6 +940,7 @@
     params.cpos++;
   } else {
     RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]);
+    CHECK(static_cast<bool>(src));
     *overlap = src.Overlaps(tgt);
 
     if (ReadBlocks(src, params.buffer, params.fd) == -1) {
@@ -948,6 +953,7 @@
     }
 
     RangeSet locs = RangeSet::Parse(params.tokens[params.cpos++]);
+    CHECK(static_cast<bool>(locs));
     MoveRange(params.buffer, locs, params.buffer);
   }
 
@@ -970,6 +976,7 @@
     }
 
     RangeSet locs = RangeSet::Parse(tokens[1]);
+    CHECK(static_cast<bool>(locs));
     MoveRange(params.buffer, locs, stash);
   }
 
@@ -1034,6 +1041,7 @@
 
   // <tgt_range>
   tgt = RangeSet::Parse(params.tokens[params.cpos++]);
+  CHECK(static_cast<bool>(tgt));
 
   std::vector<uint8_t> tgtbuffer(tgt.blocks() * BLOCKSIZE);
   if (ReadBlocks(tgt, tgtbuffer, params.fd) == -1) {
@@ -1146,6 +1154,7 @@
   }
 
   RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]);
+  CHECK(static_cast<bool>(src));
 
   allocate(src.blocks() * BLOCKSIZE, params.buffer);
   if (ReadBlocks(src, params.buffer, params.fd) == -1) {
@@ -1196,6 +1205,7 @@
   }
 
   RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
+  CHECK(static_cast<bool>(tgt));
 
   LOG(INFO) << "  zeroing " << tgt.blocks() << " blocks";
 
@@ -1238,6 +1248,7 @@
   }
 
   RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
+  CHECK(static_cast<bool>(tgt));
 
   if (params.canwrite) {
     LOG(INFO) << " writing " << tgt.blocks() << " blocks of new data";
@@ -1307,7 +1318,7 @@
 
       RangeSinkWriter writer(params.fd, tgt);
       if (params.cmdname[0] == 'i') {  // imgdiff
-        if (ApplyImagePatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value,
+        if (ApplyImagePatch(params.buffer.data(), blocks * BLOCKSIZE, patch_value,
                             std::bind(&RangeSinkWriter::Write, &writer, std::placeholders::_1,
                                       std::placeholders::_2),
                             nullptr, nullptr) != 0) {
@@ -1316,7 +1327,7 @@
           return -1;
         }
       } else {
-        if (ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, 0,
+        if (ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, patch_value, 0,
                              std::bind(&RangeSinkWriter::Write, &writer, std::placeholders::_1,
                                        std::placeholders::_2),
                              nullptr) != 0) {
@@ -1368,6 +1379,7 @@
   }
 
   RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
+  CHECK(static_cast<bool>(tgt));
 
   if (params.canwrite) {
     LOG(INFO) << " erasing " << tgt.blocks() << " blocks";
@@ -1773,6 +1785,7 @@
   }
 
   RangeSet rs = RangeSet::Parse(ranges->data);
+  CHECK(static_cast<bool>(rs));
 
   SHA_CTX ctx;
   SHA1_Init(&ctx);
@@ -1884,6 +1897,11 @@
     ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name);
     return StringValue("");
   }
+  RangeSet rs = RangeSet::Parse(ranges->data);
+  if (!rs) {
+    ErrorAbort(state, kArgsParsingFailure, "failed to parse ranges: %s", ranges->data.c_str());
+    return StringValue("");
+  }
 
   // Output notice to log when recover is attempted
   LOG(INFO) << filename->data << " image corrupted, attempting to recover...";
@@ -1909,7 +1927,7 @@
   }
 
   uint8_t buffer[BLOCKSIZE];
-  for (const auto& range : RangeSet::Parse(ranges->data)) {
+  for (const auto& range : rs) {
     for (size_t j = range.first; j < range.second; ++j) {
       // Stay within the data area, libfec validates and corrects metadata
       if (status.data_size <= static_cast<uint64_t>(j) * BLOCKSIZE) {
diff --git a/updater/install.cpp b/updater/install.cpp
index 9425d18..a111f4b 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -49,7 +49,6 @@
 #include <applypatch/applypatch.h>
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
-#include <ext4_utils/make_ext4fs.h>
 #include <ext4_utils/wipe.h>
 #include <openssl/sha.h>
 #include <selinux/label.h>
@@ -284,14 +283,8 @@
 
     int status = exec_cmd(mke2fs_argv[0], const_cast<char**>(mke2fs_argv));
     if (status != 0) {
-      LOG(WARNING) << name << ": mke2fs failed (" << status << ") on " << location
-                   << ", falling back to make_ext4fs";
-      status = make_ext4fs(location.c_str(), size, mount_point.c_str(), sehandle);
-      if (status != 0) {
-        LOG(ERROR) << name << ": make_ext4fs failed (" << status << ") on " << location;
-        return StringValue("");
-      }
-      return StringValue(location);
+      LOG(ERROR) << name << ": mke2fs failed (" << status << ") on " << location;
+      return StringValue("");
     }
 
     const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static", "-e",   "-a", mount_point.c_str(),
@@ -310,10 +303,16 @@
     std::string num_sectors = std::to_string(size / 512);
 
     const char* f2fs_path = "/sbin/mkfs.f2fs";
-    const char* f2fs_argv[] = {
-      "mkfs.f2fs", "-t", "-d1", location.c_str(), (size < 512) ? nullptr : num_sectors.c_str(),
-      nullptr
-    };
+    const char* f2fs_argv[] = { "mkfs.f2fs",
+                                "-d1",
+                                "-f",
+                                "-O",
+                                "encrypt",
+                                "-O",
+                                "quota",
+                                location.c_str(),
+                                (size < 512) ? nullptr : num_sectors.c_str(),
+                                nullptr };
     int status = exec_cmd(f2fs_path, const_cast<char**>(f2fs_argv));
     if (status != 0) {
       LOG(ERROR) << name << ": mkfs.f2fs failed (" << status << ") on " << location;