Merge "getevent: O_RDWR -> O_RDONLY | O_CLOEXEC"
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index cd42b56..fda3889 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -51,7 +51,7 @@
 
     int statusCode = capture_shell_command("getprop ro.build.version.sdk", &sdkVersionOutputBuffer,
                                            &sdkVersionErrorBuffer);
-    if (statusCode == 0 && statusCode == 0 && sdkVersionOutputBuffer.size() > 0) {
+    if (statusCode == 0 && sdkVersionOutputBuffer.size() > 0) {
         api_level = strtol((char*)sdkVersionOutputBuffer.data(), NULL, 10);
     }
 
@@ -107,7 +107,7 @@
         const char* kChmodCommandPattern = "chmod 777 %sdeployagent.sh";
         std::string chmodCommand =
                 android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentPath);
-        int ret = send_shell_command(chmodCommand.c_str());
+        int ret = send_shell_command(chmodCommand);
         return (ret == 0);
     } else {
         return false;
@@ -248,7 +248,7 @@
     std::vector<char> extractErrorBuffer;
     int statusCode;
     DeployAgentFileCallback cb(outputFp, &extractErrorBuffer, &statusCode);
-    int ret = send_shell_command(extractCommand.c_str(), false, &cb);
+    int ret = send_shell_command(extractCommand, false, &cb);
 
     if (ret == 0) {
         return cb.getBytesWritten();
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 3414d53..f32b5d5 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// This is required because no Android.bp can include a library defined in an
+// Android.mk. Eventually should kill libfastboot (defined in Android.mk)
 cc_library_host_static {
     name: "libfastboot2",
 
@@ -56,6 +58,23 @@
       linux: {
         srcs: ["usb_linux.cpp"],
       },
+
+      darwin: {
+        srcs: ["usb_osx.cpp"],
+
+        host_ldlibs: [
+            "-framework CoreFoundation",
+            "-framework IOKit",
+        ],
+      },
+
+      windows: {
+        srcs: ["usb_windows.cpp"],
+
+        host_ldlibs: [
+            "-lws2_32",
+        ],
+      },
     },
 
     cflags: [
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index a679143..7da0a9f 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -64,6 +64,7 @@
 LOCAL_C_INCLUDES_windows := development/host/windows/usb/api
 LOCAL_CFLAGS := $(fastboot_cflags)
 LOCAL_CFLAGS_darwin := $(fastboot_cflags_darwin)
+LOCAL_CPP_STD := c++17
 LOCAL_CXX_STL := $(fastboot_stl)
 LOCAL_HEADER_LIBRARIES := bootimg_headers
 LOCAL_LDLIBS_darwin := $(fastboot_ldlibs_darwin)
@@ -82,7 +83,6 @@
 
 LOCAL_CFLAGS := $(fastboot_cflags)
 LOCAL_CFLAGS_darwin := $(fastboot_cflags_darwin)
-LOCAL_CPP_STD := c++17
 LOCAL_CXX_STL := $(fastboot_stl)
 LOCAL_HEADER_LIBRARIES := bootimg_headers
 LOCAL_LDLIBS_darwin := $(fastboot_ldlibs_darwin)
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index db6d5d6..20c3359 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -104,9 +104,10 @@
     void* data;
     int64_t sz;
     int fd;
+    int64_t image_size;
 };
 
-static struct {
+struct Image {
     const char* nickname;
     const char* img_name;
     const char* sig_name;
@@ -114,7 +115,9 @@
     bool optional_if_no_image;
     bool optional_if_no_partition;
     bool IsSecondary() const { return nickname == nullptr; }
-} images[] = {
+};
+
+static Image images[] = {
         // clang-format off
     { "boot",     "boot.img",         "boot.sig",     "boot",     false, false },
     { nullptr,    "boot_other.img",   "boot.sig",     "boot",     true,  false },
@@ -128,7 +131,6 @@
                                                       "product_services",
                                                                   true,  true  },
     { "recovery", "recovery.img",     "recovery.sig", "recovery", true,  false },
-    { "super",    "super.img",        "super.sig",    "super",    true,  true  },
     { "system",   "system.img",       "system.sig",   "system",   false, true  },
     { nullptr,    "system_other.img", "system.sig",   "system",   true,  false },
     { "vbmeta",   "vbmeta.img",       "vbmeta.sig",   "vbmeta",   true,  false },
@@ -773,6 +775,13 @@
         return false;
     }
 
+    if (sparse_file* s = sparse_file_import_auto(fd, false, false)) {
+        buf->image_size = sparse_file_len(s, false, false);
+        sparse_file_destroy(s);
+    } else {
+        buf->image_size = sz;
+    }
+
     lseek64(fd, 0, SEEK_SET);
     int64_t limit = get_sparse_limit(sz);
     if (limit) {
@@ -1044,6 +1053,11 @@
     }
 }
 
+static bool is_userspace_fastboot() {
+    std::string value;
+    return fb_getvar("is-userspace", &value) && value == "yes";
+}
+
 static bool if_partition_exists(const std::string& partition, const std::string& slot) {
     std::string has_slot;
     std::string partition_name = partition;
@@ -1158,7 +1172,42 @@
     fb_queue_command("signature", "installing signature");
 }
 
-static void do_flashall(const std::string& slot_override, bool skip_secondary) {
+static bool is_logical(const std::string& partition) {
+    std::string value;
+    return fb_getvar("is-logical:" + partition, &value) && value == "yes";
+}
+
+static void update_super_partition(bool force_wipe) {
+    if (!if_partition_exists("super", "")) {
+        return;
+    }
+    std::string image = find_item_given_name("super_empty.img");
+    if (access(image.c_str(), R_OK) < 0) {
+        return;
+    }
+
+    if (!is_userspace_fastboot()) {
+        die("Must have userspace fastboot to flash logical partitions");
+    }
+
+    int fd = open(image.c_str(), O_RDONLY);
+    if (fd < 0) {
+        die("could not open '%s': %s", image.c_str(), strerror(errno));
+    }
+    fb_queue_download_fd("super", fd, get_file_size(fd));
+
+    std::string command = "update-super:super";
+    if (force_wipe) {
+        command += ":wipe";
+    }
+    fb_queue_command(command, "Updating super partition");
+
+    // We need these commands to have finished before proceeding, since
+    // otherwise "getvar is-logical" may not return a correct answer below.
+    fb_execute_queue();
+}
+
+static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe) {
     std::string fname;
     queue_info_dump();
 
@@ -1188,6 +1237,10 @@
         }
     }
 
+    update_super_partition(wipe);
+
+    // List of partitions to flash and their slots.
+    std::vector<std::pair<const Image*, std::string>> entries;
     for (size_t i = 0; i < arraysize(images); i++) {
         const char* slot = NULL;
         if (images[i].IsSecondary()) {
@@ -1196,21 +1249,38 @@
             slot = slot_override.c_str();
         }
         if (!slot) continue;
-        fname = find_item_given_name(images[i].img_name);
+        entries.emplace_back(&images[i], slot);
+
+        // Resize any logical partition to 0, so each partition is reset to 0
+        // extents, and will achieve more optimal allocation.
+        auto resize_partition = [](const std::string& partition) -> void {
+            if (is_logical(partition)) {
+                fb_queue_resize_partition(partition, "0");
+            }
+        };
+        do_for_partitions(images[i].part_name, slot, resize_partition, false);
+    }
+
+    // Flash each partition in the list if it has a corresponding image.
+    for (const auto& [image, slot] : entries) {
+        fname = find_item_given_name(image->img_name);
         fastboot_buffer buf;
         if (!load_buf(fname.c_str(), &buf)) {
-            if (images[i].optional_if_no_image) continue;
-            die("could not load '%s': %s", images[i].img_name, strerror(errno));
+            if (image->optional_if_no_image) continue;
+            die("could not load '%s': %s", image->img_name, strerror(errno));
         }
-        if (images[i].optional_if_no_partition &&
-            !if_partition_exists(images[i].part_name, slot)) {
+        if (image->optional_if_no_partition &&
+            !if_partition_exists(image->part_name, slot)) {
             continue;
         }
         auto flashall = [&](const std::string &partition) {
             do_send_signature(fname.c_str());
+            if (is_logical(partition)) {
+                fb_queue_resize_partition(partition, std::to_string(buf.image_size));
+            }
             flash_buf(partition.c_str(), &buf);
         };
-        do_for_partitions(images[i].part_name, slot, flashall, false);
+        do_for_partitions(image->part_name, slot, flashall, false);
     }
 
     if (slot_override == "all") {
@@ -1648,9 +1718,9 @@
         } else if (command == "flashall") {
             if (slot_override == "all") {
                 fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
-                do_flashall(slot_override, true);
+                do_flashall(slot_override, true, wants_wipe);
             } else {
-                do_flashall(slot_override, skip_secondary);
+                do_flashall(slot_override, skip_secondary, wants_wipe);
             }
             wants_reboot = true;
         } else if (command == "update") {
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index b0ac2ef..fc6a16b 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -8,7 +8,7 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#ifndef WIN32
+#ifndef _WIN32
 #include <sys/wait.h>
 #else
 #include <tchar.h>
@@ -27,7 +27,7 @@
 using android::base::StringPrintf;
 using android::base::unique_fd;
 
-#ifdef WIN32
+#ifdef _WIN32
 static int exec_cmd(const char* path, const char** argv, const char** envp) {
     std::string cmd;
     int i = 0;
diff --git a/fastboot/fuzzy_fastboot/Android.bp b/fastboot/fuzzy_fastboot/Android.bp
index 9a68ff3..301534b 100644
--- a/fastboot/fuzzy_fastboot/Android.bp
+++ b/fastboot/fuzzy_fastboot/Android.bp
@@ -28,4 +28,14 @@
     "libsparse",
   ],
 
+  // Static libs (libfastboot2) shared library dependencies are not transitively included
+  // This is needed to avoid link time errors when building for mac
+  target: {
+    darwin: {
+      host_ldlibs: [
+          "-framework CoreFoundation",
+          "-framework IOKit",
+      ],
+    },
+  }
 }
diff --git a/fastboot/fuzzy_fastboot/fixtures.h b/fastboot/fuzzy_fastboot/fixtures.h
index efbf43c..e47d0fd 100644
--- a/fastboot/fuzzy_fastboot/fixtures.h
+++ b/fastboot/fuzzy_fastboot/fixtures.h
@@ -133,4 +133,6 @@
 
 class UserdataPartition : public ExtensionsPartition<true> {};
 
+class SparseTestPartition : public ExtensionsPartition<true> {};
+
 }  // end namespace fastboot
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index 8dd46bc..14bf5bf 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -80,6 +80,9 @@
 std::vector<std::pair<std::string, extension::Configuration::PackedInfoTest>>
         PACKED_XML_SUCCESS_TESTS;
 std::vector<std::pair<std::string, extension::Configuration::PackedInfoTest>> PACKED_XML_FAIL_TESTS;
+// This only has 1 or zero elements so it will disappear from gtest when empty
+std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
+        SINGLE_PARTITION_XML_WRITE_HASHABLE;
 
 const std::string DEFAULT_OUPUT_NAME = "out.img";
 // const char scratch_partition[] = "userdata";
@@ -135,6 +138,22 @@
     return true;
 }
 
+bool SparseToBuf(sparse_file* sf, std::vector<char>* out, bool with_crc = false) {
+    int64_t len = sparse_file_len(sf, true, with_crc);
+    if (len <= 0) {
+        return false;
+    }
+    out->clear();
+    auto cb = [](void* priv, const void* data, size_t len) {
+        auto vec = static_cast<std::vector<char>*>(priv);
+        const char* cbuf = static_cast<const char*>(data);
+        vec->insert(vec->end(), cbuf, cbuf + len);
+        return 0;
+    };
+
+    return !sparse_file_callback(sf, true, with_crc, cb, out);
+}
+
 // Only allow alphanumeric, _, -, and .
 const auto not_allowed = [](char c) -> int {
     return !(isalnum(c) || c == '_' || c == '-' || c == '.');
@@ -524,6 +543,42 @@
     EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
 }
 
+TEST_F(Conformance, SparseVersionCheck) {
+    SparseWrapper sparse(4096, 4096);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    std::vector<char> buf;
+    ASSERT_TRUE(SparseToBuf(*sparse, &buf)) << "Sparse buffer creation failed";
+    // Invalid, right after magic
+    buf[4] = 0xff;
+    ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
+    ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
+
+    // It can either reject this download or reject it during flash
+    if (HandleResponse() != DEVICE_FAIL) {
+        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+                << "Flashing an invalid sparse version should fail " << sparse.Rep();
+    }
+}
+
+TEST_F(Conformance, SparseCRCCheck) {
+    SparseWrapper sparse(4096, 4096);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    std::vector<char> buf = RandomBuf(4096);
+    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+    ASSERT_TRUE(SparseToBuf(*sparse, &buf, true)) << "Sparse buffer creation failed";
+    // Flip a bit in the crc
+    buf.back() = buf.back() ^ 0x01;
+    ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
+    ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
+    printf("%02x\n", (unsigned char)buf.back());
+    // It can either reject this download or reject it during flash
+    if (HandleResponse() != DEVICE_FAIL) {
+        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+                << "Flashing an invalid sparse version should fail " << sparse.Rep();
+    }
+}
+
 TEST_F(UnlockPermissions, Download) {
     std::vector<char> buf{'a', 'o', 's', 'p'};
     EXPECT_EQ(fb->Download(buf), SUCCESS) << "Download 4-byte payload failed";
@@ -767,6 +822,47 @@
     }
 }
 
+TEST_F(Fuzz, SparseZeroLength) {
+    SparseWrapper sparse(4096, 0);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    RetCode ret = fb->Download(*sparse);
+    // Two ways to handle it
+    if (ret != DEVICE_FAIL) {  // if lazily parsed it better fail on a flash
+        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+                << "Flashing zero length sparse image did not fail: " << sparse.Rep();
+    }
+    ret = fb->Download(*sparse, true);
+    if (ret != DEVICE_FAIL) {  // if lazily parsed it better fail on a flash
+        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+                << "Flashing zero length sparse image did not fail " << sparse.Rep();
+    }
+}
+
+TEST_F(Fuzz, SparseTooManyChunks) {
+    SparseWrapper sparse(4096, 4096);  // 1 block, but we send two chunks that will use 2 blocks
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    std::vector<char> buf = RandomBuf(4096);
+    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+    // We take advantage of the fact the sparse library does not check this
+    ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, 4096, 1), 0)
+            << "Adding fill to sparse file failed: " << sparse.Rep();
+
+    RetCode ret = fb->Download(*sparse);
+    // Two ways to handle it
+    if (ret != DEVICE_FAIL) {  // if lazily parsed it better fail on a flash
+        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+                << "Flashing sparse image with 'total_blks' in header 1 too small did not fail "
+                << sparse.Rep();
+    }
+    ret = fb->Download(*sparse, true);
+    if (ret != DEVICE_FAIL) {  // if lazily parsed it better fail on a flash
+        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+                << "Flashing sparse image with 'total_blks' in header 1 too small did not fail "
+                << sparse.Rep();
+    }
+}
+
 TEST_F(Fuzz, USBResetSpam) {
     auto start = std::chrono::high_resolution_clock::now();
     std::chrono::duration<double> elapsed;
@@ -1379,6 +1475,109 @@
 
 INSTANTIATE_TEST_CASE_P(XMLOEM, ExtensionsOemConformance, ::testing::ValuesIn(OEM_XML_TESTS));
 
+// Sparse Tests
+TEST_P(SparseTestPartition, SparseSingleBlock) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+    const std::string part_name = name + (part_info.slots ? "_a" : "");
+    SparseWrapper sparse(4096, 4096);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    std::vector<char> buf = RandomBuf(4096);
+    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+
+    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+    std::string hash, hash_new, err_msg;
+    int retcode;
+    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+    // Now flash it the non-sparse way
+    EXPECT_EQ(fb->FlashPartition(part_name, buf), SUCCESS) << "Flashing image failed: ";
+    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+
+    EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
+                                 "methods did not result in the same hash";
+}
+
+TEST_P(SparseTestPartition, SparseFill) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+    const std::string part_name = name + (part_info.slots ? "_a" : "");
+    int64_t size = (max_dl / 4096) * 4096;
+    SparseWrapper sparse(4096, size);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, size, 0), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+
+    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+    std::string hash, hash_new, err_msg;
+    int retcode;
+    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+    // Now flash it the non-sparse way
+    std::vector<char> buf(size);
+    for (auto iter = buf.begin(); iter < buf.end(); iter += 4) {
+        iter[0] = 0xef;
+        iter[1] = 0xbe;
+        iter[2] = 0xad;
+        iter[3] = 0xde;
+    }
+    EXPECT_EQ(fb->FlashPartition(part_name, buf), SUCCESS) << "Flashing image failed: ";
+    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+
+    EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
+                                 "methods did not result in the same hash";
+}
+
+// This tests to make sure it does not overwrite previous flashes
+TEST_P(SparseTestPartition, SparseMultiple) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+    const std::string part_name = name + (part_info.slots ? "_a" : "");
+    int64_t size = (max_dl / 4096) * 4096;
+    SparseWrapper sparse(4096, size / 2);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, size / 2, 0), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+
+    SparseWrapper sparse2(4096, size / 2);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    std::vector<char> buf = RandomBuf(size / 2);
+    ASSERT_EQ(sparse_file_add_data(*sparse2, buf.data(), buf.size(), (size / 2) / 4096), 0)
+            << "Adding data failed to sparse file: " << sparse2.Rep();
+    EXPECT_EQ(fb->Download(*sparse2), SUCCESS) << "Download sparse failed: " << sparse2.Rep();
+    EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse2.Rep();
+
+    std::string hash, hash_new, err_msg;
+    int retcode;
+    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+    // Now flash it the non-sparse way
+    std::vector<char> fbuf(size);
+    for (auto iter = fbuf.begin(); iter < fbuf.begin() + size / 2; iter += 4) {
+        iter[0] = 0xef;
+        iter[1] = 0xbe;
+        iter[2] = 0xad;
+        iter[3] = 0xde;
+    }
+    fbuf.assign(buf.begin(), buf.end());
+    EXPECT_EQ(fb->FlashPartition(part_name, fbuf), SUCCESS) << "Flashing image failed: ";
+    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+
+    EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
+                                 "methods did not result in the same hash";
+}
+
+INSTANTIATE_TEST_CASE_P(XMLSparseTest, SparseTestPartition,
+                        ::testing::ValuesIn(SINGLE_PARTITION_XML_WRITE_HASHABLE));
+
 void GenerateXmlTests(const extension::Configuration& config) {
     // Build the getvar tests
     for (const auto it : config.getvars) {
@@ -1430,6 +1629,10 @@
                 std::make_tuple(part_info->first, part_info->second));
     }
 
+    if (!PARTITION_XML_WRITE_HASHABLE.empty()) {
+        SINGLE_PARTITION_XML_WRITE_HASHABLE.push_back(PARTITION_XML_WRITE_HASHABLE.front());
+    }
+
     // Build oem tests
     for (const auto it : config.oem) {
         auto oem_cmd = it.second;
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 78151d5..fb57715 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -16,6 +16,7 @@
 
 #include <dirent.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <linux/fs.h>
 #include <selinux/selinux.h>
 #include <stdio.h>
@@ -23,7 +24,9 @@
 #include <sys/mount.h>
 #include <sys/param.h>
 #include <sys/stat.h>
+#include <sys/statvfs.h>
 #include <sys/types.h>
+#include <sys/vfs.h>
 #include <unistd.h>
 
 #include <map>
@@ -35,6 +38,8 @@
 #include <android-base/macros.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
 #include <fs_mgr_overlayfs.h>
 #include <fstab/fstab.h>
 
@@ -96,9 +101,26 @@
     return "";
 }
 
+// At less than 1% free space return value of false,
+// means we will try to wrap with overlayfs.
+bool fs_mgr_filesystem_has_space(const char* mount_point) {
+    // If we have access issues to find out space remaining, return true
+    // to prevent us trying to override with overlayfs.
+    struct statvfs vst;
+    if (statvfs(mount_point, &vst)) return true;
+
+    static constexpr int percent = 1;  // 1%
+
+    return (vst.f_bfree >= (vst.f_blocks * percent / 100));
+}
+
 bool fs_mgr_overlayfs_enabled(const struct fstab_rec* fsrec) {
     // readonly filesystem, can not be mount -o remount,rw
-    return "squashfs"s == fsrec->fs_type;
+    // if squashfs or if free space is (near) zero making such a remount
+    // virtually useless, or if there are shared blocks that prevent remount,rw
+    return ("squashfs"s == fsrec->fs_type) ||
+           fs_mgr_has_shared_blocks(fsrec->mount_point, fsrec->blk_device) ||
+           !fs_mgr_filesystem_has_space(fsrec->mount_point);
 }
 
 constexpr char upper_name[] = "upper";
@@ -175,6 +197,7 @@
 
     auto fsrec_mount_point = fsrec->mount_point;
     if (!fsrec_mount_point) return false;
+    if (!fsrec->blk_device) return false;
 
     if (!fsrec->fs_type) return false;
 
@@ -477,3 +500,25 @@
 }
 
 #endif  // ALLOW_ADBD_DISABLE_VERITY != 0
+
+bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev) {
+    struct statfs fs;
+    if ((statfs((mount_point + "/lost+found").c_str(), &fs) == -1) ||
+        (fs.f_type != EXT4_SUPER_MAGIC)) {
+        return false;
+    }
+
+    android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));
+    if (fd < 0) return false;
+
+    struct ext4_super_block sb;
+    if ((TEMP_FAILURE_RETRY(lseek64(fd, 1024, SEEK_SET)) < 0) ||
+        (TEMP_FAILURE_RETRY(read(fd, &sb, sizeof(sb))) < 0)) {
+        return false;
+    }
+
+    struct fs_info info;
+    if (ext4_parse_sb(&sb, &info) < 0) return false;
+
+    return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;
+}
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 1d2ff03..ceb45de 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -18,7 +18,10 @@
 
 #include <fstab/fstab.h>
 
+#include <string>
+
 bool fs_mgr_overlayfs_mount_all();
 bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
                             bool* change = nullptr);
 bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
+bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev);
diff --git a/init/Android.bp b/init/Android.bp
index 84a78e2..a2c49d0 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -59,33 +59,24 @@
         },
     },
     static_libs: [
-        "libbootloader_message",
-        "libfs_mgr",
-        "libfec",
-        "libfec_rs",
-        "libhidl-gen-utils",
-        "libsquashfs_utils",
-        "liblogwrap",
-        "libext4_utils",
         "libseccomp_policy",
-        "libcrypto_utils",
-        "libsparse",
         "libprocessgroup",
         "libavb",
-        "libkeyutils",
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
     ],
     shared_libs: [
-        "libcutils",
         "libbase",
-        "libc",
-        "liblog",
-        "libcrypto",
-        "libc++",
+        "libbootloader_message",
+        "libcutils",
         "libdl",
-        "libz",
+        "libext4_utils",
+        "libfs_mgr",
+        "libhidl-gen-utils",
+        "libkeyutils",
+        "liblog",
+        "liblogwrap",
         "libselinux",
     ],
 }
@@ -160,6 +151,7 @@
 cc_test {
     name: "init_tests",
     defaults: ["init_defaults"],
+    compile_multilib: "first",
     srcs: [
         "devices_test.cpp",
         "init_test.cpp",
diff --git a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
index 8c5cff6..0956fe6 100644
--- a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
+++ b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
@@ -167,7 +167,7 @@
         // TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately.
         return translate_error(rc);
     } else {
-        ALOGE("Received %d byte response\n", rsp_size);
+        ALOGV("Received %d byte response\n", rsp_size);
     }
 
     const uint8_t* p = recv_buf;