Merge changes from topic "vab-brotli"

* changes:
  libsnapshot: Add an append mode to CowWriter.
  libsnapshot: Add support for brotli compression.
diff --git a/adb/README.md b/adb/README.md
new file mode 100644
index 0000000..224387c
--- /dev/null
+++ b/adb/README.md
@@ -0,0 +1,94 @@
+# ADB Internals
+
+If you are new to adb source code, you should start by reading [OVERVIEW.TXT](OVERVIEW.TXT) which describes the three components of adb pipeline.
+
+This document is here to boost what can be achieved within a "window of naive interest". You will not find function or class documentation here but rather the "big picture" which should allow you to build a mental map to help navigate the code.
+
+## Three components of adb pipeline
+
+As outlined in the overview, this codebase generates three components (Client, Server (a.k.a Host), and Daemon (a.k.a adbd)). The central part is the Server which runs on the Host computer. On one side the Server exposes a "Smart Socket" to Clients such as adb or DDMLIB. On the other side, the Server continuously monitors for connecting Daemons (as USB devices or TCP emulator). Communication with a device is done with a Transport.
+
+```
++----------+              +------------------------+
+|   ADB    +----------+   |      ADB SERVER        |                   +----------+
+|  CLIENT  |          |   |                        |              (USB)|   ADBD   |
++----------+          |   |                     Transport+-------------+ (DEVICE) |
+                      |   |                        |                   +----------+
++-----------          |   |                        |
+|   ADB    |          v   +                        |                   +----------+
+|  CLIENT  +--------->SmartSocket                  |              (USB)|   ADBD   |
++----------+          ^   | (TCP/IP)            Transport+-------------+ (DEVICE) |
+                      |   |                        |                   +----------+
++----------+          |   |                        |
+|  DDMLIB  |          |   |                     Transport+--+          +----------+
+|  CLIENT  +----------+   |                        |        |  (TCP/IP)|   ADBD   |
++----------+              +------------------------+        +----------|(EMULATOR)|
+                                                                       +----------+
+```
+
+The Client and the Server are contained in the same executable and both run on the Host machine. Code sections specific to the Host is enclosed within `ADB_HOST` guard. adbd runs on the Android Device. Daemon specific code is enclosed in `!ADB_HOST` but also sometimes with-in `__ANDROID__` guard.
+
+
+## "SMART SOCKET" and TRANSPORT
+
+A smart socket is a simple TCP socket with a smart protocol built on top of it. This is what Clients connect onto from the Host side. The Client must always initiate communication via a human readable request but the response format varies. The smart protocol is documented in [SERVICES.TXT](SERVICES.TXT).
+
+On the other side, the Server communicate with a device via a Transport. adb initially targeted devices connecting over USB, which is restricted to a fixed number of data streams. Therefore, adb multiplexes multiple byte streams over a single pipe via Transport. When devices connecting over other mechanisms (e.g. emulators over TCP) were introduced, the existing transport protocol was maintained.
+
+## THREADING MODEL and FDEVENT system
+
+At the heart of both the Server and Daemon is a main thread running an fdevent loop, which is an platform-independent abstraction over poll/epoll/WSAPoll monitoring file descriptors events. Requests and services are usually server from the main thread but some service requests result in new threads being spawned.
+
+To allow for operations to run on the Main thread, fdevent features a RunQueue combined with an interrupt fd to force polling to return.
+
+```
++------------+    +-------------------------^
+|  RUNQUEUE  |    |                         |
++------------+    |  POLLING (Main thread)  |
+| Function<> |    |                         |
++------------+    |                         |
+| Function<> |    ^-^-------^-------^------^^
++------------+      |       |       |       |
+|    ...     |      |       |       |       |
++------------+      |       |       |       |
+|            |      |       |       |       |
+|============|      |       |       |       |
+|Interrupt fd+------+  +----+  +----+  +----+
++------------+         fd      Socket  Pipe
+```
+
+## ASOCKET, APACKET, and AMESSAGE
+
+The asocket, apacket, and amessage constructs exist only to wrap data while it transits on a Transport. An asocket handles a stream of apackets. An apacket consists in a amessage header featuring a command (`A_SYNC`, `A_OPEN`, `A_CLSE`, `A_WRTE`, `A_OKAY`, ...) followed by a payload (find more documentation in [protocol.txt](protocol.txt). There is no `A_READ` command because an asocket is unidirectional. To model a bi-directional stream, asocket have a peer which go in the opposite direction.
+
+An asocket features a buffer where the elemental unit is an apacket. Is traffic is inbound, the buffer stores apacket until they are consumed. If the traffic is oubound, the buffer store apackets until they are sent down the wire (with `A_WRTE` commands).
+
+```
++---------------------ASocket------------------------+
+ |                                                   |
+ | +----------------APacket Queue------------------+ |
+ | |                                               | |
+ | |            APacket     APacket     APacket    | |
+ | |          +--------+  +--------+  +--------+   | |
+ | |          |AMessage|  |AMessage|  |AMessage|   | |
+ | |          +--------+  +--------+  +--------+   | |
+ | |          |        |  |        |  |        |   | |
+ | |  .....   |        |  |        |  |        |   | |
+ | |          |  Data  |  |  Data  |  |  Data  |   | |
+ | |          |        |  |        |  |        |   | |
+ | |          |        |  |        |  |        |   | |
+ | |          +--------+  +--------+  +--------+   | |
+ | |                                               | |
+ | +-----------------------------------------------+ |
+ +---------------------------------------------------+
+```
+
+This system allows to multiplex data streams on an unique byte stream.  Without entering too much into details, the amessage fields arg1 and arg2 are used alike in the TCP protocol where local and remote ports identify an unique stream. Note that unlike TCP which feature an "unacknowledged-send window", an apacket is sent only after the previous one has been confirmed to be received.
+
+The two types of asocket (Remote and Local) differentiate between outbound and inbound traffic.
+
+## adbd <-> APPPLICATION communication
+
+This pipeline is detailed in [services.cpp](services.cpp). The JDWP extension implemented by Dalvik/ART are documented in:
+- platform/dalvik/+/master/docs/debugmon.html
+- platform/dalvik/+/master/docs/debugger.html
diff --git a/fastboot/fastboot.bash b/fastboot/fastboot.bash
index a46f420..406e8b8 100644
--- a/fastboot/fastboot.bash
+++ b/fastboot/fastboot.bash
@@ -109,7 +109,7 @@
 
     cur="${COMP_WORDS[COMP_CWORD]}"
     if [[ $i -eq $COMP_CWORD ]]; then
-        partitions="boot bootloader dtbo modem modules odm odm_dlkm oem product radio recovery system vbmeta vendor vendor_dlkm"
+        partitions="boot bootloader dtbo modem odm odm_dlkm oem product radio recovery system vbmeta vendor vendor_dlkm"
         COMPREPLY=( $(compgen -W "$partitions" -- $cur) )
     else
         _fastboot_util_complete_local_file "${cur}" '!*.img'
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 3969367..4bf791e 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -144,7 +144,6 @@
     { "cache",    "cache.img",        "cache.sig",    "cache",    true,  ImageType::Extra },
     { "dtbo",     "dtbo.img",         "dtbo.sig",     "dtbo",     true,  ImageType::BootCritical },
     { "dts",      "dt.img",           "dt.sig",       "dts",      true,  ImageType::BootCritical },
-    { "modules",  "modules.img",      "modules.sig",  "modules",  true,  ImageType::Normal },
     { "odm",      "odm.img",          "odm.sig",      "odm",      true,  ImageType::Normal },
     { "odm_dlkm", "odm_dlkm.img",     "odm_dlkm.sig", "odm_dlkm", true,  ImageType::Normal },
     { "product",  "product.img",      "product.sig",  "product",  true,  ImageType::Normal },
@@ -527,12 +526,15 @@
 
 static bool UnzipToMemory(ZipArchiveHandle zip, const std::string& entry_name,
                           std::vector<char>* out) {
-    ZipEntry zip_entry;
+    ZipEntry64 zip_entry;
     if (FindEntry(zip, entry_name, &zip_entry) != 0) {
         fprintf(stderr, "archive does not contain '%s'\n", entry_name.c_str());
         return false;
     }
 
+    if (zip_entry.uncompressed_length > std::numeric_limits<size_t>::max()) {
+      die("entry '%s' is too large: %" PRIu64, entry_name.c_str(), zip_entry.uncompressed_length);
+    }
     out->resize(zip_entry.uncompressed_length);
 
     fprintf(stderr, "extracting %s (%zu MB) to RAM...\n", entry_name.c_str(),
@@ -638,14 +640,14 @@
 static int unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
     unique_fd fd(make_temporary_fd(entry_name));
 
-    ZipEntry zip_entry;
+    ZipEntry64 zip_entry;
     if (FindEntry(zip, entry_name, &zip_entry) != 0) {
         fprintf(stderr, "archive does not contain '%s'\n", entry_name);
         errno = ENOENT;
         return -1;
     }
 
-    fprintf(stderr, "extracting %s (%" PRIu32 " MB) to disk...", entry_name,
+    fprintf(stderr, "extracting %s (%" PRIu64 " MB) to disk...", entry_name,
             zip_entry.uncompressed_length / 1024 / 1024);
     double start = now();
     int error = ExtractEntryToFile(zip, &zip_entry, fd);
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 7912688..bb7d8b3 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -427,6 +427,20 @@
     return true;
 }
 
+// Accepts a device mapper device name (like system_a, vendor_b etc) and
+// returns its UUID.
+bool DeviceMapper::GetDmDeviceUuidByName(const std::string& name, std::string* uuid) {
+    struct dm_ioctl io;
+    InitIo(&io, name);
+    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+        PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
+        return false;
+    }
+
+    *uuid = std::string(io.uuid);
+    return true;
+}
+
 bool DeviceMapper::GetDeviceNumber(const std::string& name, dev_t* dev) {
     struct dm_ioctl io;
     InitIo(&io, name);
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index abe9c4c..5d6db46 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -172,6 +172,13 @@
     // could race with ueventd.
     bool GetDmDevicePathByName(const std::string& name, std::string* path);
 
+    // Returns the device mapper UUID for a given name.  If the device does not
+    // exist, false is returned, and the path parameter is not set.
+    //
+    // WaitForFile() should not be used in conjunction with this call, since it
+    // could race with ueventd.
+    bool GetDmDeviceUuidByName(const std::string& name, std::string* path);
+
     // Returns a device's unique path as generated by ueventd. This will return
     // true as long as the device has been created, even if ueventd has not
     // processed it yet.
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
index 4c8ff5f..80acb4a 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -37,52 +37,86 @@
 class SnapuserdTest : public ::testing::Test {
   protected:
     void SetUp() override {
-        cow_ = std::make_unique<TemporaryFile>();
-        ASSERT_GE(cow_->fd, 0) << strerror(errno);
+        cow_system_ = std::make_unique<TemporaryFile>();
+        ASSERT_GE(cow_system_->fd, 0) << strerror(errno);
+
+        cow_product_ = std::make_unique<TemporaryFile>();
+        ASSERT_GE(cow_product_->fd, 0) << strerror(errno);
+
+        size_ = 100_MiB;
     }
 
-    void TearDown() override { cow_ = nullptr; }
+    void TearDown() override {
+        cow_system_ = nullptr;
+        cow_product_ = nullptr;
+    }
 
-    std::unique_ptr<TemporaryFile> cow_;
+    std::unique_ptr<TemporaryFile> cow_system_;
+    std::unique_ptr<TemporaryFile> cow_product_;
+
+    unique_fd sys_fd_;
+    unique_fd product_fd_;
+    size_t size_;
+
+    int system_blksize_;
+    int product_blksize_;
+    std::string system_device_name_;
+    std::string product_device_name_;
+
+    std::unique_ptr<uint8_t[]> random_buffer_1_;
+    std::unique_ptr<uint8_t[]> random_buffer_2_;
+    std::unique_ptr<uint8_t[]> zero_buffer_;
+    std::unique_ptr<uint8_t[]> system_buffer_;
+    std::unique_ptr<uint8_t[]> product_buffer_;
+
+    void Init();
+    void CreateCowDevice(std::unique_ptr<TemporaryFile>& cow);
+    void CreateSystemDmUser();
+    void CreateProductDmUser();
+    void StartSnapuserdDaemon();
+    void CreateSnapshotDevices();
+
+    void TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>&& buf);
 };
 
-TEST_F(SnapuserdTest, ReadWrite) {
-    loff_t offset = 0;
-    size_t size = 100_MiB;
+void SnapuserdTest::Init() {
     unique_fd rnd_fd;
-    unique_fd sys_fd;
-    unique_fd snapshot_fd;
-    unique_fd system_a_fd;
-    std::string cmd;
+    loff_t offset = 0;
 
     rnd_fd.reset(open("/dev/random", O_RDONLY));
     ASSERT_TRUE(rnd_fd > 0);
 
-    std::unique_ptr<uint8_t[]> random_buffer_1;
-    std::unique_ptr<uint8_t[]> random_buffer_2;
-    std::unique_ptr<uint8_t[]> system_buffer;
-
-    random_buffer_1 = std::make_unique<uint8_t[]>(size);
-
-    random_buffer_2 = std::make_unique<uint8_t[]>(size);
-
-    system_buffer = std::make_unique<uint8_t[]>(size);
+    random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
+    random_buffer_2_ = std::make_unique<uint8_t[]>(size_);
+    system_buffer_ = std::make_unique<uint8_t[]>(size_);
+    product_buffer_ = std::make_unique<uint8_t[]>(size_);
+    zero_buffer_ = std::make_unique<uint8_t[]>(size_);
 
     // Fill random data
-    for (size_t j = 0; j < (size / 1_MiB); j++) {
-        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1.get() + offset, 1_MiB, 0), true);
+    for (size_t j = 0; j < (size_ / 1_MiB); j++) {
+        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
+                  true);
 
-        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_2.get() + offset, 1_MiB, 0), true);
+        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_2_.get() + offset, 1_MiB, 0),
+                  true);
 
         offset += 1_MiB;
     }
 
-    sys_fd.reset(open("/dev/block/mapper/system_a", O_RDONLY));
-    ASSERT_TRUE(sys_fd > 0);
+    sys_fd_.reset(open("/dev/block/mapper/system_a", O_RDONLY));
+    ASSERT_TRUE(sys_fd_ > 0);
+
+    product_fd_.reset(open("/dev/block/mapper/product_a", O_RDONLY));
+    ASSERT_TRUE(product_fd_ > 0);
 
     // Read from system partition from offset 0 of size 100MB
-    ASSERT_EQ(ReadFullyAtOffset(sys_fd, system_buffer.get(), size, 0), true);
+    ASSERT_EQ(ReadFullyAtOffset(sys_fd_, system_buffer_.get(), size_, 0), true);
 
+    // Read from system partition from offset 0 of size 100MB
+    ASSERT_EQ(ReadFullyAtOffset(product_fd_, product_buffer_.get(), size_, 0), true);
+}
+
+void SnapuserdTest::CreateCowDevice(std::unique_ptr<TemporaryFile>& cow) {
     //================Create a COW file with the following operations===========
     //
     // Create COW file which is gz compressed
@@ -96,12 +130,12 @@
     options.compression = "gz";
     CowWriter writer(options);
 
-    ASSERT_TRUE(writer.Initialize(cow_->fd));
+    ASSERT_TRUE(writer.Initialize(cow->fd));
 
     // Write 100MB random data to COW file which is gz compressed from block 0
-    ASSERT_TRUE(writer.AddRawBlocks(0, random_buffer_1.get(), size));
+    ASSERT_TRUE(writer.AddRawBlocks(0, random_buffer_1_.get(), size_));
 
-    size_t num_blocks = size / options.block_size;
+    size_t num_blocks = size_ / options.block_size;
     size_t blk_start_copy = num_blocks;
     size_t blk_end_copy = blk_start_copy + num_blocks;
     size_t source_blk = 0;
@@ -110,7 +144,7 @@
     // has to read from block 0 in system_a partition
     //
     // This initializes copy operation from block 0 of size 100 MB from
-    // /dev/block/mapper/system_a
+    // /dev/block/mapper/system_a or product_a
     for (size_t i = blk_start_copy; i < blk_end_copy; i++) {
         ASSERT_TRUE(writer.AddCopy(i, source_blk));
         source_blk += 1;
@@ -125,14 +159,17 @@
     // Final 100MB filled with random data which is gz compressed
     size_t blk_random2_replace_start = blk_zero_copy_end;
 
-    ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_2.get(), size));
+    ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_2_.get(), size_));
 
     // Flush operations
     ASSERT_TRUE(writer.Flush());
 
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+    ASSERT_EQ(lseek(cow->fd, 0, SEEK_SET), 0);
+}
 
-    //================Setup dm-snapshot and start snapuserd daemon===========
+void SnapuserdTest::CreateSystemDmUser() {
+    unique_fd system_a_fd;
+    std::string cmd;
 
     // Create a COW device. Number of sectors is chosen random which can
     // hold at least 400MB of data
@@ -140,39 +177,77 @@
     system_a_fd.reset(open("/dev/block/mapper/system_a", O_RDONLY));
     ASSERT_TRUE(system_a_fd > 0);
 
-    int blksize;
-    int err = ioctl(system_a_fd.get(), BLKGETSIZE, &blksize);
-    if (err < 0) {
-        ASSERT_TRUE(0);
-    }
+    int err = ioctl(system_a_fd.get(), BLKGETSIZE, &system_blksize_);
+    ASSERT_GE(err, 0);
 
-    cmd = "dmctl create system_cow user 0 " + std::to_string(blksize);
+    std::string str(cow_system_->path);
+    std::size_t found = str.find_last_of("/\\");
+    ASSERT_NE(found, std::string::npos);
+    system_device_name_ = str.substr(found + 1);
+    cmd = "dmctl create " + system_device_name_ + " user 0 " + std::to_string(system_blksize_);
+
     system(cmd.c_str());
+}
 
+void SnapuserdTest::CreateProductDmUser() {
+    unique_fd product_a_fd;
+    std::string cmd;
+
+    // Create a COW device. Number of sectors is chosen random which can
+    // hold at least 400MB of data
+
+    product_a_fd.reset(open("/dev/block/mapper/product_a", O_RDONLY));
+    ASSERT_TRUE(product_a_fd > 0);
+
+    int err = ioctl(product_a_fd.get(), BLKGETSIZE, &product_blksize_);
+    ASSERT_GE(err, 0);
+
+    std::string str(cow_product_->path);
+    std::size_t found = str.find_last_of("/\\");
+    ASSERT_NE(found, std::string::npos);
+    product_device_name_ = str.substr(found + 1);
+    cmd = "dmctl create " + product_device_name_ + " user 0 " + std::to_string(product_blksize_);
+
+    system(cmd.c_str());
+}
+
+void SnapuserdTest::StartSnapuserdDaemon() {
     // Start the snapuserd daemon
     if (fork() == 0) {
-        const char* argv[] = {"/system/bin/snapuserd", cow_->path, "/dev/block/mapper/system_a",
-                              nullptr};
+        const char* argv[] = {"/system/bin/snapuserd",       cow_system_->path,
+                              "/dev/block/mapper/system_a",  cow_product_->path,
+                              "/dev/block/mapper/product_a", nullptr};
         if (execv(argv[0], const_cast<char**>(argv))) {
             ASSERT_TRUE(0);
         }
     }
+}
+
+void SnapuserdTest::CreateSnapshotDevices() {
+    std::string cmd;
+
+    cmd = "dmctl create system-snapshot -ro snapshot 0 " + std::to_string(system_blksize_);
+    cmd += " /dev/block/mapper/system_a";
+    cmd += " /dev/block/mapper/" + system_device_name_;
+    cmd += " P 8";
+
+    system(cmd.c_str());
 
     cmd.clear();
 
-    cmd = "dmctl create system-snapshot -ro snapshot 0 " + std::to_string(blksize);
-    cmd += " /dev/block/mapper/system_a /dev/block/mapper/system_cow ";
-    cmd += "P 8";
+    cmd = "dmctl create product-snapshot -ro snapshot 0 " + std::to_string(product_blksize_);
+    cmd += " /dev/block/mapper/product_a";
+    cmd += " /dev/block/mapper/" + product_device_name_;
+    cmd += " P 8";
+
     system(cmd.c_str());
+}
 
-    // Wait so that snapshot device is created
-    sleep(5);
-    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size);
+void SnapuserdTest::TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>&& buf) {
+    loff_t offset = 0;
+    std::unique_ptr<uint8_t[]> buffer = std::move(buf);
 
-    offset = 0;
-
-    snapshot_fd.reset(open("/dev/block/mapper/system-snapshot", O_RDONLY));
-    ASSERT_TRUE(snapshot_fd > 0);
+    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
 
     //================Start IO operation on dm-snapshot device=================
     // This will test the following paths:
@@ -189,16 +264,16 @@
     // dm-snap->dm-snap-persistent->dm-user->snapuserd->read_compressed_cow (replace
     // op)->decompress_cow->return
 
-    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size, offset), true);
+    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
 
     // Update the offset
-    offset += size;
+    offset += size_;
 
-    // Compare data with random_buffer_1.
-    ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_1.get(), size), 0);
+    // Compare data with random_buffer_1_.
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_1_.get(), size_), 0);
 
     // Clear the buffer
-    memset(snapuserd_buffer.get(), 0, size);
+    memset(snapuserd_buffer.get(), 0, size_);
 
     // Read from snapshot device of size 100MB from offset 100MB. This tests the
     // copy operation.
@@ -207,13 +282,13 @@
     //
     // dm-snap->dm-snap-persistent->dm-user->snapuserd->read_from_system_a_partition
     // (copy op) -> return
-    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size, offset), true);
+    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
 
     // Update the offset
-    offset += size;
+    offset += size_;
 
-    // Compare data with system_buffer.
-    ASSERT_EQ(memcmp(snapuserd_buffer.get(), system_buffer.get(), size), 0);
+    // Compare data with buffer.
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), buffer.get(), size_), 0);
 
     // Read from snapshot device of size 100MB from offset 200MB. This tests the
     // zero operation.
@@ -222,16 +297,13 @@
     //
     // dm-snap->dm-snap-persistent->dm-user->snapuserd->fill_memory_with_zero
     // (zero op) -> return
-    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size, offset), true);
-
-    // Fill the random_buffer_1 with zero as we no longer need it
-    memset(random_buffer_1.get(), 0, size);
+    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
 
     // Compare data with zero filled buffer
-    ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_1.get(), size), 0);
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), zero_buffer_.get(), size_), 0);
 
     // Update the offset
-    offset += size;
+    offset += size_;
 
     // Read from snapshot device of size 100MB from offset 300MB. This tests the
     // final replace operation.
@@ -240,10 +312,34 @@
     //
     // dm-snap->dm-snap-persistent->dm-user->snapuserd->read_compressed_cow (replace
     // op)->decompress_cow->return
-    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size, offset), true);
+    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
 
-    // Compare data with random_buffer_2.
-    ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_2.get(), size), 0);
+    // Compare data with random_buffer_2_.
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_2_.get(), size_), 0);
+}
+
+TEST_F(SnapuserdTest, ReadWrite) {
+    unique_fd snapshot_fd;
+
+    Init();
+
+    CreateCowDevice(cow_system_);
+    CreateCowDevice(cow_product_);
+
+    CreateSystemDmUser();
+    CreateProductDmUser();
+
+    StartSnapuserdDaemon();
+
+    CreateSnapshotDevices();
+
+    snapshot_fd.reset(open("/dev/block/mapper/system-snapshot", O_RDONLY));
+    ASSERT_TRUE(snapshot_fd > 0);
+    TestIO(snapshot_fd, std::move(system_buffer_));
+
+    snapshot_fd.reset(open("/dev/block/mapper/product-snapshot", O_RDONLY));
+    ASSERT_TRUE(snapshot_fd > 0);
+    TestIO(snapshot_fd, std::move(product_buffer_));
 }
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
index 605af9b..d3f4f70 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -17,10 +17,12 @@
 #include <linux/types.h>
 #include <stdlib.h>
 
+#include <csignal>
 #include <cstring>
 #include <iostream>
 #include <limits>
 #include <string>
+#include <thread>
 #include <vector>
 
 #include <android-base/file.h>
@@ -35,6 +37,7 @@
 namespace android {
 namespace snapshot {
 
+using namespace android;
 using namespace android::dm;
 using android::base::unique_fd;
 
@@ -45,6 +48,60 @@
 
 static_assert(PAYLOAD_SIZE >= BLOCK_SIZE);
 
+class Target {
+  public:
+    // Represents an already-created Target, which is referenced by UUID.
+    Target(std::string uuid) : uuid_(uuid) {}
+
+    const auto& uuid() { return uuid_; }
+    std::string control_path() { return std::string("/dev/dm-user-") + uuid(); }
+
+  private:
+    const std::string uuid_;
+};
+
+class Daemon {
+    // The Daemon class is a singleton to avoid
+    // instantiating more than once
+  public:
+    static Daemon& Instance() {
+        static Daemon instance;
+        return instance;
+    }
+
+    bool IsRunning();
+
+  private:
+    bool is_running_;
+
+    Daemon();
+    Daemon(Daemon const&) = delete;
+    void operator=(Daemon const&) = delete;
+
+    static void SignalHandler(int signal);
+};
+
+Daemon::Daemon() {
+    is_running_ = true;
+    signal(SIGINT, Daemon::SignalHandler);
+    signal(SIGTERM, Daemon::SignalHandler);
+}
+
+bool Daemon::IsRunning() {
+    return is_running_;
+}
+
+void Daemon::SignalHandler(int signal) {
+    LOG(DEBUG) << "Snapuserd received signal: " << signal;
+    switch (signal) {
+        case SIGINT:
+        case SIGTERM: {
+            Daemon::Instance().is_running_ = false;
+            break;
+        }
+    }
+}
+
 class BufferSink : public IByteSink {
   public:
     void Initialize(size_t size) {
@@ -558,10 +615,26 @@
         return 1;
     }
 
-    // TODO: use UUID to support multiple partitions
-    ctrl_fd_.reset(open("/dev/dm-user", O_RDWR));
+    std::string str(in_cow_device_);
+    std::size_t found = str.find_last_of("/\\");
+    CHECK(found != std::string::npos);
+    std::string device_name = str.substr(found + 1);
+
+    LOG(DEBUG) << "Fetching UUID for: " << device_name;
+
+    auto& dm = dm::DeviceMapper::Instance();
+    std::string uuid;
+    if (!dm.GetDmDeviceUuidByName(device_name, &uuid)) {
+        LOG(ERROR) << "Unable to find UUID for " << in_cow_device_;
+        return 1;
+    }
+
+    LOG(DEBUG) << "UUID: " << uuid;
+    Target t(uuid);
+
+    ctrl_fd_.reset(open(t.control_path().c_str(), O_RDWR));
     if (ctrl_fd_ < 0) {
-        LOG(ERROR) << "Unable to open /dev/dm-user";
+        LOG(ERROR) << "Unable to open " << t.control_path();
         return 1;
     }
 
@@ -682,9 +755,30 @@
 }  // namespace snapshot
 }  // namespace android
 
+void run_thread(std::string cow_device, std::string backing_device) {
+    android::snapshot::Snapuserd snapd(cow_device, backing_device);
+    snapd.Run();
+}
+
 int main([[maybe_unused]] int argc, char** argv) {
     android::base::InitLogging(argv, &android::base::KernelLogger);
-    android::snapshot::Snapuserd snapd(argv[1], argv[2]);
 
-    return snapd.Run();
+    android::snapshot::Daemon& daemon = android::snapshot::Daemon::Instance();
+
+    while (daemon.IsRunning()) {
+        // TODO: This is hardcoded wherein:
+        // argv[1] = system_cow, argv[2] = /dev/block/mapper/system_a
+        // argv[3] = product_cow, argv[4] = /dev/block/mapper/product_a
+        //
+        // This should be fixed based on some kind of IPC or setup a
+        // command socket and spin up the thread based when a new
+        // partition is visible.
+        std::thread system_a(run_thread, argv[1], argv[2]);
+        std::thread product_a(run_thread, argv[3], argv[4]);
+
+        system_a.join();
+        product_a.join();
+    }
+
+    return 0;
 }
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 7a3d9a9..9edcda7 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -38,6 +38,7 @@
 #include <vector>
 
 using namespace std::literals::string_literals;
+using namespace std::chrono_literals;
 using namespace android::dm;
 using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
 
@@ -49,6 +50,7 @@
     std::cerr << "  delete <dm-name>" << std::endl;
     std::cerr << "  list <devices | targets> [-v]" << std::endl;
     std::cerr << "  getpath <dm-name>" << std::endl;
+    std::cerr << "  getuuid <dm-name>" << std::endl;
     std::cerr << "  info <dm-name>" << std::endl;
     std::cerr << "  status <dm-name>" << std::endl;
     std::cerr << "  resume <dm-name>" << std::endl;
@@ -241,8 +243,9 @@
         return ret;
     }
 
+    std::string ignore_path;
     DeviceMapper& dm = DeviceMapper::Instance();
-    if (!dm.CreateDevice(name, table)) {
+    if (!dm.CreateDevice(name, table, &ignore_path, 5s)) {
         std::cerr << "Failed to create device-mapper device with name: " << name << std::endl;
         return -EIO;
     }
@@ -391,6 +394,22 @@
     return 0;
 }
 
+static int GetUuidCmdHandler(int argc, char** argv) {
+    if (argc != 1) {
+        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+        return -EINVAL;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    std::string uuid;
+    if (!dm.GetDmDeviceUuidByName(argv[0], &uuid)) {
+        std::cerr << "Could not query uuid of device \"" << argv[0] << "\"." << std::endl;
+        return -EINVAL;
+    }
+    std::cout << uuid << std::endl;
+    return 0;
+}
+
 static int InfoCmdHandler(int argc, char** argv) {
     if (argc != 1) {
         std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
@@ -504,6 +523,7 @@
         {"list", DmListCmdHandler},
         {"help", HelpCmdHandler},
         {"getpath", GetPathCmdHandler},
+        {"getuuid", GetUuidCmdHandler},
         {"info", InfoCmdHandler},
         {"table", TableCmdHandler},
         {"status", StatusCmdHandler},
diff --git a/include/private/android_filesystem_capability.h b/include/private/android_filesystem_capability.h
deleted file mode 120000
index f310b35..0000000
--- a/include/private/android_filesystem_capability.h
+++ /dev/null
@@ -1 +0,0 @@
-../../libcutils/include/private/android_filesystem_capability.h
\ No newline at end of file
diff --git a/include/private/android_logger.h b/include/private/android_logger.h
deleted file mode 120000
index f187a6d..0000000
--- a/include/private/android_logger.h
+++ /dev/null
@@ -1 +0,0 @@
-../../liblog/include/private/android_logger.h
\ No newline at end of file
diff --git a/include/private/canned_fs_config.h b/include/private/canned_fs_config.h
deleted file mode 120000
index 8f92b2d..0000000
--- a/include/private/canned_fs_config.h
+++ /dev/null
@@ -1 +0,0 @@
-../../libcutils/include/private/canned_fs_config.h
\ No newline at end of file
diff --git a/include/private/fs_config.h b/include/private/fs_config.h
deleted file mode 100644
index e9868a4..0000000
--- a/include/private/fs_config.h
+++ /dev/null
@@ -1,4 +0,0 @@
-// TODO(b/63135587) remove this file after the transitive dependency
-// from private/android_filesystem_config.h is resolved. All files that use
-// libcutils/include/private/fs_config.h should include the file directly, not
-// indirectly via private/android_filesystem_config.h.
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
index bf79ea2..89cdfe5 100644
--- a/libstats/socket/Android.bp
+++ b/libstats/socket/Android.bp
@@ -29,6 +29,7 @@
     static_libs: [
         "libcutils", // does not expose a stable C API
     ],
+    header_libs: ["liblog_headers"],
     cflags: [
         "-Wall",
         "-Werror",
diff --git a/logd/Android.bp b/logd/Android.bp
index 7f67ab0..fe22d1c 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -66,6 +66,7 @@
         "SerializedLogChunk.cpp",
         "SimpleLogBuffer.cpp",
     ],
+    static_libs: ["liblog"],
     logtags: ["event.logtags"],
 
     export_include_dirs: ["."],
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 81dba1f..77fa94e 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -141,9 +141,6 @@
 # via /odm/lib/modules directly.
 LOCAL_POST_INSTALL_CMD += ; ln -sf /odm/odm_dlkm/etc $(TARGET_ROOT_OUT)/odm_dlkm/etc
 
-# For /modules partition.
-LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/modules
-
 ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
 else