Merge "Convert gsid to use the dynamic AIDL service infrastructure" into rvc-dev
diff --git a/adb/Android.bp b/adb/Android.bp
index a26017f..6fd0767 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -429,7 +429,7 @@
     },
 }
 
-cc_library_static {
+cc_library {
     name: "libadbd_services",
     defaults: ["adbd_defaults", "host_adbd_supported"],
     recovery_available: true,
@@ -464,6 +464,7 @@
         "libbase",
         "libcrypto",
         "libcrypto_utils",
+        "libcutils_sockets",
         "liblog",
     ],
 
@@ -515,6 +516,7 @@
         "libadb_tls_connection",
         "libadbd_auth",
         "libadbd_fs",
+        "libadbd_services",
         "libasyncio",
         "libbase",
         "libcrypto",
@@ -533,7 +535,6 @@
     },
 
     static_libs: [
-        "libadbd_services",
         "libcutils_sockets",
         "libdiagnose_usb",
         "libmdnssd",
@@ -575,10 +576,8 @@
         "libcrypto_utils",
         "libcutils_sockets",
         "libdiagnose_usb",
-        "liblog",
         "libmdnssd",
         "libminijail",
-        "libselinux",
         "libssl",
     ],
 
@@ -588,6 +587,8 @@
         "libadbd_auth",
         "libadbd_fs",
         "libcrypto",
+        "liblog",
+        "libselinux",
     ],
 
     target: {
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index 922f2ba..cc38926 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -29,6 +29,7 @@
 #include <utime.h>
 
 #include <chrono>
+#include <deque>
 #include <functional>
 #include <memory>
 #include <sstream>
@@ -203,7 +204,7 @@
 
 class SyncConnection {
   public:
-    SyncConnection() : expect_done_(false) {
+    SyncConnection() {
         max = SYNC_DATA_MAX; // TODO: decide at runtime.
 
         std::string error;
@@ -239,16 +240,6 @@
 
     bool IsValid() { return fd >= 0; }
 
-    bool ReceivedError(const char* from, const char* to) {
-        adb_pollfd pfd = {.fd = fd.get(), .events = POLLIN};
-        int rc = adb_poll(&pfd, 1, 0);
-        if (rc < 0) {
-            Error("failed to poll: %s", strerror(errno));
-            return true;
-        }
-        return rc != 0;
-    }
-
     void NewTransfer() {
         current_ledger_.Reset();
     }
@@ -258,6 +249,11 @@
         global_ledger_.bytes_transferred += bytes;
     }
 
+    void RecordFileSent(std::string from, std::string to) {
+        RecordFilesTransferred(1);
+        deferred_acknowledgements_.emplace_back(std::move(from), std::move(to));
+    }
+
     void RecordFilesTransferred(size_t files) {
         current_ledger_.files_transferred += files;
         global_ledger_.files_transferred += files;
@@ -283,39 +279,38 @@
         }
     }
 
-    bool SendRequest(int id, const char* path_and_mode) {
-        size_t path_length = strlen(path_and_mode);
-        if (path_length > 1024) {
-            Error("SendRequest failed: path too long: %zu", path_length);
+    bool SendRequest(int id, const std::string& path) {
+        if (path.length() > 1024) {
+            Error("SendRequest failed: path too long: %zu", path.length());
             errno = ENAMETOOLONG;
             return false;
         }
 
         // Sending header and payload in a single write makes a noticeable
         // difference to "adb sync" performance.
-        std::vector<char> buf(sizeof(SyncRequest) + path_length);
+        std::vector<char> buf(sizeof(SyncRequest) + path.length());
         SyncRequest* req = reinterpret_cast<SyncRequest*>(&buf[0]);
         req->id = id;
-        req->path_length = path_length;
+        req->path_length = path.length();
         char* data = reinterpret_cast<char*>(req + 1);
-        memcpy(data, path_and_mode, path_length);
+        memcpy(data, path.data(), path.length());
 
-        return WriteFdExactly(fd, &buf[0], buf.size());
+        return WriteFdExactly(fd, buf.data(), buf.size());
     }
 
-    bool SendStat(const char* path_and_mode) {
+    bool SendStat(const std::string& path) {
         if (!have_stat_v2_) {
             errno = ENOTSUP;
             return false;
         }
-        return SendRequest(ID_STAT_V2, path_and_mode);
+        return SendRequest(ID_STAT_V2, path);
     }
 
-    bool SendLstat(const char* path_and_mode) {
+    bool SendLstat(const std::string& path) {
         if (have_stat_v2_) {
-            return SendRequest(ID_LSTAT_V2, path_and_mode);
+            return SendRequest(ID_LSTAT_V2, path);
         } else {
-            return SendRequest(ID_LSTAT_V1, path_and_mode);
+            return SendRequest(ID_LSTAT_V1, path);
         }
     }
 
@@ -374,7 +369,7 @@
         return true;
     }
 
-    bool SendLs(const char* path) {
+    bool SendLs(const std::string& path) {
         return SendRequest(have_ls_v2_ ? ID_LIST_V2 : ID_LIST_V1, path);
     }
 
@@ -415,28 +410,26 @@
 
     // Sending header, payload, and footer in a single write makes a huge
     // difference to "adb sync" performance.
-    bool SendSmallFile(const char* path_and_mode,
-                       const char* lpath, const char* rpath,
-                       unsigned mtime,
-                       const char* data, size_t data_length) {
-        size_t path_length = strlen(path_and_mode);
-        if (path_length > 1024) {
-            Error("SendSmallFile failed: path too long: %zu", path_length);
+    bool SendSmallFile(const std::string& path, mode_t mode, const std::string& lpath,
+                       const std::string& rpath, unsigned mtime, const char* data,
+                       size_t data_length) {
+        std::string path_and_mode = android::base::StringPrintf("%s,%d", path.c_str(), mode);
+        if (path_and_mode.length() > 1024) {
+            Error("SendSmallFile failed: path too long: %zu", path_and_mode.length());
             errno = ENAMETOOLONG;
             return false;
         }
 
-        std::vector<char> buf(sizeof(SyncRequest) + path_length +
-                              sizeof(SyncRequest) + data_length +
-                              sizeof(SyncRequest));
+        std::vector<char> buf(sizeof(SyncRequest) + path_and_mode.length() + sizeof(SyncRequest) +
+                              data_length + sizeof(SyncRequest));
         char* p = &buf[0];
 
         SyncRequest* req_send = reinterpret_cast<SyncRequest*>(p);
         req_send->id = ID_SEND;
-        req_send->path_length = path_length;
+        req_send->path_length = path_and_mode.length();
         p += sizeof(SyncRequest);
-        memcpy(p, path_and_mode, path_length);
-        p += path_length;
+        memcpy(p, path_and_mode.data(), path_and_mode.size());
+        p += path_and_mode.length();
 
         SyncRequest* req_data = reinterpret_cast<SyncRequest*>(p);
         req_data->id = ID_DATA;
@@ -451,34 +444,34 @@
         p += sizeof(SyncRequest);
 
         WriteOrDie(lpath, rpath, &buf[0], (p - &buf[0]));
-        expect_done_ = true;
 
-        // RecordFilesTransferred gets called in CopyDone.
+        RecordFileSent(lpath, rpath);
         RecordBytesTransferred(data_length);
         ReportProgress(rpath, data_length, data_length);
         return true;
     }
 
-    bool SendLargeFile(const char* path_and_mode,
-                       const char* lpath, const char* rpath,
-                       unsigned mtime) {
+    bool SendLargeFile(const std::string& path, mode_t mode, const std::string& lpath,
+                       const std::string& rpath, unsigned mtime) {
+        std::string path_and_mode = android::base::StringPrintf("%s,%d", path.c_str(), mode);
         if (!SendRequest(ID_SEND, path_and_mode)) {
-            Error("failed to send ID_SEND message '%s': %s", path_and_mode, strerror(errno));
+            Error("failed to send ID_SEND message '%s': %s", path_and_mode.c_str(),
+                  strerror(errno));
             return false;
         }
 
         struct stat st;
-        if (stat(lpath, &st) == -1) {
-            Error("cannot stat '%s': %s", lpath, strerror(errno));
+        if (stat(lpath.c_str(), &st) == -1) {
+            Error("cannot stat '%s': %s", lpath.c_str(), strerror(errno));
             return false;
         }
 
         uint64_t total_size = st.st_size;
         uint64_t bytes_copied = 0;
 
-        unique_fd lfd(adb_open(lpath, O_RDONLY));
+        unique_fd lfd(adb_open(lpath.c_str(), O_RDONLY));
         if (lfd < 0) {
-            Error("opening '%s' locally failed: %s", lpath, strerror(errno));
+            Error("opening '%s' locally failed: %s", lpath.c_str(), strerror(errno));
             return false;
         }
 
@@ -487,7 +480,7 @@
         while (true) {
             int bytes_read = adb_read(lfd, sbuf.data, max - sizeof(SyncRequest));
             if (bytes_read == -1) {
-                Error("reading '%s' locally failed: %s", lpath, strerror(errno));
+                Error("reading '%s' locally failed: %s", lpath.c_str(), strerror(errno));
                 return false;
             } else if (bytes_read == 0) {
                 break;
@@ -499,55 +492,53 @@
             RecordBytesTransferred(bytes_read);
             bytes_copied += bytes_read;
 
-            // Check to see if we've received an error from the other side.
-            if (ReceivedError(lpath, rpath)) {
-                break;
-            }
-
             ReportProgress(rpath, bytes_copied, total_size);
         }
 
         syncmsg msg;
         msg.data.id = ID_DONE;
         msg.data.size = mtime;
-        expect_done_ = true;
-
-        // RecordFilesTransferred gets called in CopyDone.
+        RecordFileSent(lpath, rpath);
         return WriteOrDie(lpath, rpath, &msg.data, sizeof(msg.data));
     }
 
-    bool CopyDone(const char* from, const char* to) {
+    bool ReadAcknowledgments() {
+        bool result = true;
+        while (!deferred_acknowledgements_.empty()) {
+            auto [from, to] = std::move(deferred_acknowledgements_.front());
+            deferred_acknowledgements_.pop_front();
+            result &= CopyDone(from, to);
+        }
+        return result;
+    }
+
+    bool CopyDone(const std::string& from, const std::string& to) {
         syncmsg msg;
         if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
-            Error("failed to copy '%s' to '%s': couldn't read from device", from, to);
+            Error("failed to copy '%s' to '%s': couldn't read from device", from.c_str(),
+                  to.c_str());
             return false;
         }
         if (msg.status.id == ID_OKAY) {
-            if (expect_done_) {
-                expect_done_ = false;
-                RecordFilesTransferred(1);
-                return true;
-            } else {
-                Error("failed to copy '%s' to '%s': received premature success", from, to);
-                return true;
-            }
+            return true;
         }
         if (msg.status.id != ID_FAIL) {
-            Error("failed to copy '%s' to '%s': unknown reason %d", from, to, msg.status.id);
+            Error("failed to copy '%s' to '%s': unknown reason %d", from.c_str(), to.c_str(),
+                  msg.status.id);
             return false;
         }
         return ReportCopyFailure(from, to, msg);
     }
 
-    bool ReportCopyFailure(const char* from, const char* to, const syncmsg& msg) {
+    bool ReportCopyFailure(const std::string& from, const std::string& to, const syncmsg& msg) {
         std::vector<char> buf(msg.status.msglen + 1);
         if (!ReadFdExactly(fd, &buf[0], msg.status.msglen)) {
-            Error("failed to copy '%s' to '%s'; failed to read reason (!): %s",
-                  from, to, strerror(errno));
+            Error("failed to copy '%s' to '%s'; failed to read reason (!): %s", from.c_str(),
+                  to.c_str(), strerror(errno));
             return false;
         }
         buf[msg.status.msglen] = 0;
-        Error("failed to copy '%s' to '%s': remote %s", from, to, &buf[0]);
+        Error("failed to copy '%s' to '%s': remote %s", from.c_str(), to.c_str(), &buf[0]);
         return false;
     }
 
@@ -616,7 +607,7 @@
     size_t max;
 
   private:
-    bool expect_done_;
+    std::deque<std::pair<std::string, std::string>> deferred_acknowledgements_;
     FeatureSet features_;
     bool have_stat_v2_;
     bool have_ls_v2_;
@@ -629,16 +620,19 @@
         return SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
     }
 
-    bool WriteOrDie(const char* from, const char* to, const void* data, size_t data_length) {
+    bool WriteOrDie(const std::string& from, const std::string& to, const void* data,
+                    size_t data_length) {
         if (!WriteFdExactly(fd, data, data_length)) {
             if (errno == ECONNRESET) {
                 // Assume adbd told us why it was closing the connection, and
                 // try to read failure reason from adbd.
                 syncmsg msg;
                 if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
-                    Error("failed to copy '%s' to '%s': no response: %s", from, to, strerror(errno));
+                    Error("failed to copy '%s' to '%s': no response: %s", from.c_str(), to.c_str(),
+                          strerror(errno));
                 } else if (msg.status.id != ID_FAIL) {
-                    Error("failed to copy '%s' to '%s': not ID_FAIL: %d", from, to, msg.status.id);
+                    Error("failed to copy '%s' to '%s': not ID_FAIL: %d", from.c_str(), to.c_str(),
+                          msg.status.id);
                 } else {
                     ReportCopyFailure(from, to, msg);
                 }
@@ -651,20 +645,20 @@
     }
 };
 
-static bool sync_ls(SyncConnection& sc, const char* path,
+static bool sync_ls(SyncConnection& sc, const std::string& path,
                     const std::function<sync_ls_cb>& func) {
     return sc.SendLs(path) && sc.FinishLs(func);
 }
 
-static bool sync_stat(SyncConnection& sc, const char* path, struct stat* st) {
+static bool sync_stat(SyncConnection& sc, const std::string& path, struct stat* st) {
     return sc.SendStat(path) && sc.FinishStat(st);
 }
 
-static bool sync_lstat(SyncConnection& sc, const char* path, struct stat* st) {
+static bool sync_lstat(SyncConnection& sc, const std::string& path, struct stat* st) {
     return sc.SendLstat(path) && sc.FinishStat(st);
 }
 
-static bool sync_stat_fallback(SyncConnection& sc, const char* path, struct stat* st) {
+static bool sync_stat_fallback(SyncConnection& sc, const std::string& path, struct stat* st) {
     if (sync_stat(sc, path, st)) {
         return true;
     }
@@ -688,7 +682,7 @@
         struct stat tmp_st;
 
         st->st_mode &= ~S_IFMT;
-        if (sync_lstat(sc, dir_path.c_str(), &tmp_st)) {
+        if (sync_lstat(sc, dir_path, &tmp_st)) {
             st->st_mode |= S_IFDIR;
         } else {
             st->st_mode |= S_IFREG;
@@ -697,10 +691,8 @@
     return true;
 }
 
-static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath, unsigned mtime,
-                      mode_t mode, bool sync) {
-    std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
-
+static bool sync_send(SyncConnection& sc, const std::string& lpath, const std::string& rpath,
+                      unsigned mtime, mode_t mode, bool sync) {
     if (sync) {
         struct stat st;
         if (sync_lstat(sc, rpath, &st)) {
@@ -714,41 +706,40 @@
     if (S_ISLNK(mode)) {
 #if !defined(_WIN32)
         char buf[PATH_MAX];
-        ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1);
+        ssize_t data_length = readlink(lpath.c_str(), buf, PATH_MAX - 1);
         if (data_length == -1) {
-            sc.Error("readlink '%s' failed: %s", lpath, strerror(errno));
+            sc.Error("readlink '%s' failed: %s", lpath.c_str(), strerror(errno));
             return false;
         }
         buf[data_length++] = '\0';
 
-        if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime, buf, data_length)) {
+        if (!sc.SendSmallFile(rpath, mode, lpath, rpath, mtime, buf, data_length)) {
             return false;
         }
-        return sc.CopyDone(lpath, rpath);
+        return true;
 #endif
     }
 
     struct stat st;
-    if (stat(lpath, &st) == -1) {
-        sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno));
+    if (stat(lpath.c_str(), &st) == -1) {
+        sc.Error("failed to stat local file '%s': %s", lpath.c_str(), strerror(errno));
         return false;
     }
     if (st.st_size < SYNC_DATA_MAX) {
         std::string data;
         if (!android::base::ReadFileToString(lpath, &data, true)) {
-            sc.Error("failed to read all of '%s': %s", lpath, strerror(errno));
+            sc.Error("failed to read all of '%s': %s", lpath.c_str(), strerror(errno));
             return false;
         }
-        if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime,
-                              data.data(), data.size())) {
+        if (!sc.SendSmallFile(rpath, mode, lpath, rpath, mtime, data.data(), data.size())) {
             return false;
         }
     } else {
-        if (!sc.SendLargeFile(path_and_mode.c_str(), lpath, rpath, mtime)) {
+        if (!sc.SendLargeFile(rpath, mode, lpath, rpath, mtime)) {
             return false;
         }
     }
-    return sc.CopyDone(lpath, rpath);
+    return true;
 }
 
 static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath,
@@ -943,7 +934,7 @@
 
     if (check_timestamps) {
         for (const copyinfo& ci : file_list) {
-            if (!sc.SendLstat(ci.rpath.c_str())) {
+            if (!sc.SendLstat(ci.rpath)) {
                 sc.Error("failed to send lstat");
                 return false;
             }
@@ -965,7 +956,7 @@
             if (list_only) {
                 sc.Println("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str());
             } else {
-                if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode, false)) {
+                if (!sync_send(sc, ci.lpath, ci.rpath, ci.time, ci.mode, false)) {
                     return false;
                 }
             }
@@ -1069,6 +1060,7 @@
         sc.ReportTransferRate(src_path, TransferDirection::push);
     }
 
+    success &= sc.ReadAcknowledgments();
     sc.ReportOverallTransferRate(TransferDirection::push);
     return success;
 }
diff --git a/adb/client/incremental.cpp b/adb/client/incremental.cpp
index 6499d46..3ceb374 100644
--- a/adb/client/incremental.cpp
+++ b/adb/client/incremental.cpp
@@ -45,33 +45,29 @@
     return result;
 }
 
-static inline int32_t read_be_int32(borrowed_fd fd) {
-    return int32_t(be32toh(read_int32(fd)));
-}
-
 static inline void append_int(borrowed_fd fd, std::vector<char>* bytes) {
-    int32_t be_val = read_int32(fd);
+    int32_t le_val = read_int32(fd);
     auto old_size = bytes->size();
-    bytes->resize(old_size + sizeof(be_val));
-    memcpy(bytes->data() + old_size, &be_val, sizeof(be_val));
+    bytes->resize(old_size + sizeof(le_val));
+    memcpy(bytes->data() + old_size, &le_val, sizeof(le_val));
 }
 
 static inline void append_bytes_with_size(borrowed_fd fd, std::vector<char>* bytes) {
-    int32_t be_size = read_int32(fd);
-    int32_t size = int32_t(be32toh(be_size));
+    int32_t le_size = read_int32(fd);
+    int32_t size = int32_t(le32toh(le_size));
     auto old_size = bytes->size();
-    bytes->resize(old_size + sizeof(be_size) + size);
-    memcpy(bytes->data() + old_size, &be_size, sizeof(be_size));
-    ReadFully(fd, bytes->data() + old_size + sizeof(be_size), size);
+    bytes->resize(old_size + sizeof(le_size) + size);
+    memcpy(bytes->data() + old_size, &le_size, sizeof(le_size));
+    ReadFully(fd, bytes->data() + old_size + sizeof(le_size), size);
 }
 
 static inline std::pair<std::vector<char>, int32_t> read_id_sig_headers(borrowed_fd fd) {
     std::vector<char> result;
     append_int(fd, &result);              // version
-    append_bytes_with_size(fd, &result);  // verityRootHash
-    append_bytes_with_size(fd, &result);  // v3Digest
-    append_bytes_with_size(fd, &result);  // pkcs7SignatureBlock
-    auto tree_size = read_be_int32(fd);   // size of the verity tree
+    append_bytes_with_size(fd, &result);  // hashingInfo
+    append_bytes_with_size(fd, &result);  // signingInfo
+    auto le_tree_size = read_int32(fd);
+    auto tree_size = int32_t(le32toh(le_tree_size));  // size of the verity tree
     return {std::move(result), tree_size};
 }
 
diff --git a/base/liblog_symbols.cpp b/base/liblog_symbols.cpp
index d5dfcd2..8d59179 100644
--- a/base/liblog_symbols.cpp
+++ b/base/liblog_symbols.cpp
@@ -16,14 +16,20 @@
 
 #include "liblog_symbols.h"
 
-#if defined(__ANDROID__) && !defined(NO_LIBLOG_DLSYM)
+#if defined(__ANDROID__)
+#if !defined(NO_LIBLOG_DLSYM) || defined(__ANDROID_APEX__)
+#define USE_DLSYM
+#endif
+#endif
+
+#ifdef USE_DLSYM
 #include <dlfcn.h>
 #endif
 
 namespace android {
 namespace base {
 
-#if defined(__ANDROID__) && !defined(NO_LIBLOG_DLSYM)
+#ifdef USE_DLSYM
 
 const std::optional<LibLogFunctions>& GetLibLogFunctions() {
   static std::optional<LibLogFunctions> liblog_functions = []() -> std::optional<LibLogFunctions> {
diff --git a/base/liblog_symbols.h b/base/liblog_symbols.h
index d3134e9..b4ab06a 100644
--- a/base/liblog_symbols.h
+++ b/base/liblog_symbols.h
@@ -36,8 +36,8 @@
   void (*__android_log_set_aborter)(__android_aborter_function aborter);
   void (*__android_log_call_aborter)(const char* abort_message);
   void (*__android_log_default_aborter)(const char* abort_message);
-  int (*__android_log_set_minimum_priority)(int priority);
-  int (*__android_log_get_minimum_priority)();
+  int32_t (*__android_log_set_minimum_priority)(int32_t priority);
+  int32_t (*__android_log_get_minimum_priority)();
   void (*__android_log_set_default_tag)(const char* tag);
 };
 
diff --git a/base/logging.cpp b/base/logging.cpp
index 9360a56..9a6e0fb 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -118,7 +118,7 @@
 }
 #endif
 
-static LogId log_id_tToLogId(int buffer_id) {
+static LogId log_id_tToLogId(int32_t buffer_id) {
   switch (buffer_id) {
     case LOG_ID_MAIN:
       return MAIN;
@@ -134,7 +134,7 @@
   }
 }
 
-static int LogIdTolog_id_t(LogId log_id) {
+static int32_t LogIdTolog_id_t(LogId log_id) {
   switch (log_id) {
     case MAIN:
       return LOG_ID_MAIN;
@@ -171,7 +171,7 @@
   }
 }
 
-static android_LogPriority LogSeverityToPriority(LogSeverity severity) {
+static int32_t LogSeverityToPriority(LogSeverity severity) {
   switch (severity) {
     case VERBOSE:
       return ANDROID_LOG_VERBOSE;
@@ -333,12 +333,12 @@
 void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag,
                             const char* file, unsigned int line,
                             const char* message) {
-  android_LogPriority priority = LogSeverityToPriority(severity);
+  int32_t priority = LogSeverityToPriority(severity);
   if (id == DEFAULT) {
     id = default_log_id_;
   }
 
-  int lg_id = LogIdTolog_id_t(id);
+  int32_t lg_id = LogIdTolog_id_t(id);
 
   char log_message_with_file[4068];  // LOGGER_ENTRY_MAX_PAYLOAD, not available in the NDK.
   if (priority == ANDROID_LOG_FATAL && file != nullptr) {
@@ -574,7 +574,7 @@
 void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
                          const char* message) {
   static auto& liblog_functions = GetLibLogFunctions();
-  auto priority = LogSeverityToPriority(severity);
+  int32_t priority = LogSeverityToPriority(severity);
   if (liblog_functions) {
     __android_logger_data logger_data = {
         sizeof(__android_logger_data), LOG_ID_DEFAULT, priority, tag, file, line};
@@ -608,7 +608,7 @@
   // we need to fall back to using gMinimumLogSeverity, since __android_log_is_loggable() will not
   // take into consideration the value from SetMinimumLogSeverity().
   if (liblog_functions) {
-    int priority = LogSeverityToPriority(severity);
+    int32_t priority = LogSeverityToPriority(severity);
     return __android_log_is_loggable(priority, tag, ANDROID_LOG_INFO);
   } else {
     return severity >= gMinimumLogSeverity;
@@ -618,7 +618,7 @@
 LogSeverity SetMinimumLogSeverity(LogSeverity new_severity) {
   static auto& liblog_functions = GetLibLogFunctions();
   if (liblog_functions) {
-    auto priority = LogSeverityToPriority(new_severity);
+    int32_t priority = LogSeverityToPriority(new_severity);
     return PriorityToLogSeverity(liblog_functions->__android_log_set_minimum_priority(priority));
   } else {
     LogSeverity old_severity = gMinimumLogSeverity;
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index 4f24360..665d24a 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -19,7 +19,9 @@
 #include <bionic/reserved_signals.h>
 #include <signal.h>
 #include <stdint.h>
+#include <string.h>
 #include <sys/cdefs.h>
+#include <sys/system_properties.h>
 #include <sys/types.h>
 
 __BEGIN_DECLS
@@ -50,16 +52,21 @@
 #define DEBUGGER_SIGNAL BIONIC_SIGNAL_DEBUGGER
 
 static void __attribute__((__unused__)) debuggerd_register_handlers(struct sigaction* action) {
-  sigaction(SIGABRT, action, nullptr);
-  sigaction(SIGBUS, action, nullptr);
-  sigaction(SIGFPE, action, nullptr);
-  sigaction(SIGILL, action, nullptr);
-  sigaction(SIGSEGV, action, nullptr);
-#if defined(SIGSTKFLT)
-  sigaction(SIGSTKFLT, action, nullptr);
-#endif
-  sigaction(SIGSYS, action, nullptr);
-  sigaction(SIGTRAP, action, nullptr);
+  char value[PROP_VALUE_MAX] = "";
+  bool enabled =
+      !(__system_property_get("ro.debuggable", value) > 0 && !strcmp(value, "1") &&
+        __system_property_get("debug.debuggerd.disable", value) > 0 && !strcmp(value, "1"));
+  if (enabled) {
+    sigaction(SIGABRT, action, nullptr);
+    sigaction(SIGBUS, action, nullptr);
+    sigaction(SIGFPE, action, nullptr);
+    sigaction(SIGILL, action, nullptr);
+    sigaction(SIGSEGV, action, nullptr);
+    sigaction(SIGSTKFLT, action, nullptr);
+    sigaction(SIGSYS, action, nullptr);
+    sigaction(SIGTRAP, action, nullptr);
+  }
+
   sigaction(BIONIC_SIGNAL_DEBUGGER, action, nullptr);
 }
 
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 59cae61..46018b9 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -96,6 +96,7 @@
 
 using android::base::Basename;
 using android::base::GetBoolProperty;
+using android::base::Readlink;
 using android::base::Realpath;
 using android::base::SetProperty;
 using android::base::StartsWith;
@@ -736,15 +737,33 @@
     unsigned long mountflags = entry.flags;
     int ret = 0;
     int save_errno = 0;
+    int gc_allowance = 0;
+    std::string opts;
+    bool try_f2fs_gc_allowance = is_f2fs(entry.fs_type) && entry.fs_checkpoint_opts.length() > 0;
+    Timer t;
+
     do {
+        if (save_errno == EINVAL && try_f2fs_gc_allowance) {
+            PINFO << "Kernel does not support checkpoint=disable:[n]%, trying without.";
+            try_f2fs_gc_allowance = false;
+        }
+        if (try_f2fs_gc_allowance) {
+            opts = entry.fs_options + entry.fs_checkpoint_opts + ":" +
+                   std::to_string(gc_allowance) + "%";
+        } else {
+            opts = entry.fs_options;
+        }
         if (save_errno == EAGAIN) {
             PINFO << "Retrying mount (source=" << source << ",target=" << target
-                  << ",type=" << entry.fs_type << ")=" << ret << "(" << save_errno << ")";
+                  << ",type=" << entry.fs_type << ", gc_allowance=" << gc_allowance << "%)=" << ret
+                  << "(" << save_errno << ")";
         }
         ret = mount(source.c_str(), target.c_str(), entry.fs_type.c_str(), mountflags,
-                    entry.fs_options.c_str());
+                    opts.c_str());
         save_errno = errno;
-    } while (ret && save_errno == EAGAIN);
+        if (try_f2fs_gc_allowance) gc_allowance += 10;
+    } while ((ret && save_errno == EAGAIN && gc_allowance <= 100) ||
+             (ret && save_errno == EINVAL && try_f2fs_gc_allowance));
     const char* target_missing = "";
     const char* source_missing = "";
     if (save_errno == ENOENT) {
@@ -760,6 +779,8 @@
     if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
         fs_mgr_set_blk_ro(source);
     }
+    android::base::SetProperty("ro.boottime.init.mount." + Basename(target),
+                               std::to_string(t.duration().count()));
     errno = save_errno;
     return ret;
 }
@@ -1074,7 +1095,7 @@
     bool UpdateCheckpointPartition(FstabEntry* entry, const std::string& block_device) {
         if (entry->fs_mgr_flags.checkpoint_fs) {
             if (is_f2fs(entry->fs_type)) {
-                entry->fs_options += ",checkpoint=disable";
+                entry->fs_checkpoint_opts = ",checkpoint=disable";
             } else {
                 LERROR << entry->fs_type << " does not implement checkpoints.";
             }
@@ -1588,6 +1609,61 @@
     }
 }
 
+static bool UnwindDmDeviceStack(const std::string& block_device,
+                                std::vector<std::string>* dm_stack) {
+    if (!StartsWith(block_device, "/dev/block/")) {
+        LWARNING << block_device << " is not a block device";
+        return false;
+    }
+    std::string current = block_device;
+    DeviceMapper& dm = DeviceMapper::Instance();
+    while (true) {
+        dm_stack->push_back(current);
+        if (!dm.IsDmBlockDevice(current)) {
+            break;
+        }
+        auto parent = dm.GetParentBlockDeviceByPath(current);
+        if (!parent) {
+            return false;
+        }
+        current = *parent;
+    }
+    return true;
+}
+
+FstabEntry* fs_mgr_get_mounted_entry_for_userdata(Fstab* fstab, const FstabEntry& mounted_entry) {
+    if (mounted_entry.mount_point != "/data") {
+        LERROR << mounted_entry.mount_point << " is not /data";
+        return nullptr;
+    }
+    std::vector<std::string> dm_stack;
+    if (!UnwindDmDeviceStack(mounted_entry.blk_device, &dm_stack)) {
+        LERROR << "Failed to unwind dm-device stack for " << mounted_entry.blk_device;
+        return nullptr;
+    }
+    for (auto& entry : *fstab) {
+        if (entry.mount_point != "/data") {
+            continue;
+        }
+        std::string block_device;
+        if (entry.fs_mgr_flags.logical) {
+            if (!fs_mgr_update_logical_partition(&entry)) {
+                LERROR << "Failed to update logic partition " << entry.blk_device;
+                continue;
+            }
+            block_device = entry.blk_device;
+        } else if (!Readlink(entry.blk_device, &block_device)) {
+            PWARNING << "Failed to read link " << entry.blk_device;
+            block_device = entry.blk_device;
+        }
+        if (std::find(dm_stack.begin(), dm_stack.end(), block_device) != dm_stack.end()) {
+            return &entry;
+        }
+    }
+    LERROR << "Didn't find entry that was used to mount /data onto " << mounted_entry.blk_device;
+    return nullptr;
+}
+
 // TODO(b/143970043): return different error codes based on which step failed.
 int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
     Fstab proc_mounts;
@@ -1596,16 +1672,13 @@
         return -1;
     }
     std::string block_device;
-    if (auto entry = GetEntryForMountPoint(&proc_mounts, "/data"); entry != nullptr) {
-        // Note: we don't care about a userdata wrapper here, since it's safe
-        // to remount on top of the bow device instead, there will be no
-        // conflicts.
-        block_device = entry->blk_device;
-    } else {
+    auto mounted_entry = GetEntryForMountPoint(&proc_mounts, "/data");
+    if (mounted_entry == nullptr) {
         LERROR << "/data is not mounted";
         return -1;
     }
-    auto fstab_entry = GetMountedEntryForUserdata(fstab);
+    block_device = mounted_entry->blk_device;
+    auto fstab_entry = fs_mgr_get_mounted_entry_for_userdata(fstab, *mounted_entry);
     if (fstab_entry == nullptr) {
         LERROR << "Can't find /data in fstab";
         return -1;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index a36934a..f3f1cb7 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -814,89 +814,6 @@
     return entries;
 }
 
-static std::string ResolveBlockDevice(const std::string& block_device) {
-    if (!StartsWith(block_device, "/dev/block/")) {
-        LWARNING << block_device << " is not a block device";
-        return block_device;
-    }
-    std::string name = block_device.substr(5);
-    if (!StartsWith(name, "block/dm-")) {
-        // Not a dm-device, but might be a symlink. Optimistically try to readlink.
-        std::string result;
-        if (Readlink(block_device, &result)) {
-            return result;
-        } else if (errno == EINVAL) {
-            // After all, it wasn't a symlink.
-            return block_device;
-        } else {
-            LERROR << "Failed to readlink " << block_device;
-            return "";
-        }
-    }
-    // It's a dm-device, let's find what's inside!
-    std::string sys_dir = "/sys/" + name;
-    while (true) {
-        std::string slaves_dir = sys_dir + "/slaves";
-        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(slaves_dir.c_str()), closedir);
-        if (!dir) {
-            LERROR << "Failed to open " << slaves_dir;
-            return "";
-        }
-        std::string sub_device_name = "";
-        for (auto entry = readdir(dir.get()); entry; entry = readdir(dir.get())) {
-            if (entry->d_type != DT_LNK) continue;
-            if (!sub_device_name.empty()) {
-                LERROR << "Too many slaves in " << slaves_dir;
-                return "";
-            }
-            sub_device_name = entry->d_name;
-        }
-        if (sub_device_name.empty()) {
-            LERROR << "No slaves in " << slaves_dir;
-            return "";
-        }
-        if (!StartsWith(sub_device_name, "dm-")) {
-            // Not a dm-device! We can stop now.
-            return "/dev/block/" + sub_device_name;
-        }
-        // Still a dm-device, keep digging.
-        sys_dir = "/sys/block/" + sub_device_name;
-    }
-}
-
-FstabEntry* GetMountedEntryForUserdata(Fstab* fstab) {
-    Fstab mounts;
-    if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
-        LERROR << "Failed to read /proc/mounts";
-        return nullptr;
-    }
-    auto mounted_entry = GetEntryForMountPoint(&mounts, "/data");
-    if (mounted_entry == nullptr) {
-        LWARNING << "/data is not mounted";
-        return nullptr;
-    }
-    std::string resolved_block_device = ResolveBlockDevice(mounted_entry->blk_device);
-    if (resolved_block_device.empty()) {
-        return nullptr;
-    }
-    LINFO << "/data is mounted on " << resolved_block_device;
-    for (auto& entry : *fstab) {
-        if (entry.mount_point != "/data") {
-            continue;
-        }
-        std::string block_device;
-        if (!Readlink(entry.blk_device, &block_device)) {
-            LWARNING << "Failed to readlink " << entry.blk_device;
-            block_device = entry.blk_device;
-        }
-        if (block_device == resolved_block_device) {
-            return &entry;
-        }
-    }
-    LERROR << "Didn't find entry that was used to mount /data";
-    return nullptr;
-}
-
 std::set<std::string> GetBootDevices() {
     // First check the kernel commandline, then try the device tree otherwise
     std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 9bc38f9..3d556c9 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -107,6 +107,10 @@
 // it destroys verity devices from device mapper after the device is unmounted.
 int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab);
 
+// Finds a entry in |fstab| that was used to mount a /data |mounted_entry| from
+// /proc/mounts.
+android::fs_mgr::FstabEntry* fs_mgr_get_mounted_entry_for_userdata(
+        android::fs_mgr::Fstab* fstab, const android::fs_mgr::FstabEntry& mounted_entry);
 int fs_mgr_remount_userdata_into_checkpointing(android::fs_mgr::Fstab* fstab);
 
 // Finds the dm_bow device on which this block device is stacked, or returns
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index c94d7ac..7cf4f89 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -36,6 +36,7 @@
     std::string fs_type;
     unsigned long flags = 0;
     std::string fs_options;
+    std::string fs_checkpoint_opts;
     std::string key_loc;
     std::string metadata_key_dir;
     std::string metadata_encryption;
@@ -102,7 +103,6 @@
 FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
 // The Fstab can contain multiple entries for the same mount point with different configurations.
 std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path);
-FstabEntry* GetMountedEntryForUserdata(Fstab* fstab);
 
 // This method builds DSU fstab entries and transfer the fstab.
 //
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 254fbed..673e145 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -120,7 +120,7 @@
         return false;
     }
     if (!WaitForFileDeleted(unique_path, timeout_ms)) {
-        LOG(ERROR) << "Timeout out waiting for " << unique_path << " to be deleted";
+        LOG(ERROR) << "Failed waiting for " << unique_path << " to be deleted";
         return false;
     }
     return true;
@@ -161,7 +161,7 @@
         return true;
     }
     if (!WaitForFile(unique_path, timeout_ms)) {
-        LOG(ERROR) << "Timed out waiting for device path: " << unique_path;
+        LOG(ERROR) << "Failed waiting for device path: " << unique_path;
         DeleteDevice(name);
         return false;
     }
diff --git a/fs_mgr/libdm/utility.cpp b/fs_mgr/libdm/utility.cpp
index f252565..0eb59ab 100644
--- a/fs_mgr/libdm/utility.cpp
+++ b/fs_mgr/libdm/utility.cpp
@@ -19,6 +19,8 @@
 
 #include <thread>
 
+#include <android-base/logging.h>
+
 using namespace std::literals;
 
 namespace android {
@@ -45,7 +47,11 @@
         // If the file exists but returns EPERM or something, we consider the
         // condition met.
         if (access(path.c_str(), F_OK) != 0) {
-            if (errno == ENOENT) return WaitResult::Wait;
+            if (errno == ENOENT) {
+                return WaitResult::Wait;
+            }
+            PLOG(ERROR) << "access failed: " << path;
+            return WaitResult::Fail;
         }
         return WaitResult::Done;
     };
@@ -54,9 +60,13 @@
 
 bool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds& timeout_ms) {
     auto condition = [&]() -> WaitResult {
-        if (access(path.c_str(), F_OK) == 0 || errno != ENOENT) {
+        if (access(path.c_str(), F_OK) == 0) {
             return WaitResult::Wait;
         }
+        if (errno != ENOENT) {
+            PLOG(ERROR) << "access failed: " << path;
+            return WaitResult::Fail;
+        }
         return WaitResult::Done;
     };
     return WaitForCondition(condition, timeout_ms);
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 0a0a21d..d670ca0 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -96,16 +96,6 @@
     static_libs: [
         "libfs_mgr_binder"
     ],
-
-    shared_libs: [
-        // TODO(b/148818798): remove when parent bug is fixed
-        "libutilscallstack",
-    ],
-    cflags: [
-        "-g",
-        "-O0",
-        "-DLIBSNAPSHOT_USE_CALLSTACK",
-    ],
 }
 
 cc_library_static {
@@ -179,9 +169,6 @@
         "libsparse",
         "libutils",
         "libz",
-
-        // TODO(b/148818798): remove when parent bug is fixed
-        "libutilscallstack",
     ],
     static_libs: [
         "libfs_mgr",
@@ -231,8 +218,5 @@
         "libprotobuf-cpp-lite",
         "libstatslog",
         "libutils",
-
-        // TODO(b/148818798): remove when parent bug is fixed.
-        "libutilscallstack",
     ],
 }
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 32345d2..957c26c 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -74,7 +74,8 @@
 
 static constexpr const std::string_view kCowGroupName = "cow";
 
-bool SourceCopyOperationIsClone(const chromeos_update_engine::InstallOperation& operation);
+bool OptimizeSourceCopyOperation(const chromeos_update_engine::InstallOperation& operation,
+                                 chromeos_update_engine::InstallOperation* optimized);
 
 enum class CreateResult : unsigned int {
     ERROR,
@@ -125,6 +126,9 @@
     // might be needed to perform first-stage mounts.
     static bool IsSnapshotManagerNeeded();
 
+    // Helper function for second stage init to restorecon on the rollback indicator.
+    static std::string GetGlobalRollbackIndicatorPath();
+
     // Begin an update. This must be called before creating any snapshots. It
     // will fail if GetUpdateState() != None.
     bool BeginUpdate();
@@ -175,16 +179,6 @@
     UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
                                    const std::function<bool()>& before_cancel = {});
 
-    // Initiate the merge if necessary, then wait for the merge to finish.
-    // See InitiateMerge() and ProcessUpdateState() for details.
-    // Returns:
-    //   - None if no merge to initiate
-    //   - Unverified if called on the source slot
-    //   - MergeCompleted if merge is completed
-    //   - other states indicating an error has occurred
-    UpdateState InitiateMergeAndWait(SnapshotMergeReport* report = nullptr,
-                                     const std::function<bool()>& before_cancel = {});
-
     // Find the status of the current update, if any.
     //
     // |progress| depends on the returned status:
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index 61f5c0c..efdb59f 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -62,17 +62,68 @@
     return false;
 }
 
-bool SourceCopyOperationIsClone(const InstallOperation& operation) {
-    using ChromeOSExtent = chromeos_update_engine::Extent;
-    if (operation.src_extents().size() != operation.dst_extents().size()) {
+bool OptimizeSourceCopyOperation(const InstallOperation& operation, InstallOperation* optimized) {
+    if (operation.type() != InstallOperation::SOURCE_COPY) {
         return false;
     }
-    return std::equal(operation.src_extents().begin(), operation.src_extents().end(),
-                      operation.dst_extents().begin(),
-                      [](const ChromeOSExtent& src, const ChromeOSExtent& dst) {
-                          return src.start_block() == dst.start_block() &&
-                                 src.num_blocks() == dst.num_blocks();
-                      });
+
+    optimized->Clear();
+    optimized->set_type(InstallOperation::SOURCE_COPY);
+
+    const auto& src_extents = operation.src_extents();
+    const auto& dst_extents = operation.dst_extents();
+
+    // If input is empty, skip by returning an empty result.
+    if (src_extents.empty() && dst_extents.empty()) {
+        return true;
+    }
+
+    auto s_it = src_extents.begin();
+    auto d_it = dst_extents.begin();
+    uint64_t s_offset = 0;  // offset within *s_it
+    uint64_t d_offset = 0;  // offset within *d_it
+    bool is_optimized = false;
+
+    while (s_it != src_extents.end() || d_it != dst_extents.end()) {
+        if (s_it == src_extents.end() || d_it == dst_extents.end()) {
+            LOG(ERROR) << "number of blocks do not equal in src_extents and dst_extents";
+            return false;
+        }
+        if (s_it->num_blocks() <= s_offset || d_it->num_blocks() <= d_offset) {
+            LOG(ERROR) << "Offset goes out of bounds.";
+            return false;
+        }
+
+        // Check the next |step| blocks, where |step| is the min of remaining blocks in the current
+        // source extent and current destination extent.
+        auto s_step = s_it->num_blocks() - s_offset;
+        auto d_step = d_it->num_blocks() - d_offset;
+        auto step = std::min(s_step, d_step);
+
+        bool moved = s_it->start_block() + s_offset != d_it->start_block() + d_offset;
+        if (moved) {
+            // If the next |step| blocks are not copied to the same location, add them to result.
+            AppendExtent(optimized->mutable_src_extents(), s_it->start_block() + s_offset, step);
+            AppendExtent(optimized->mutable_dst_extents(), d_it->start_block() + d_offset, step);
+        } else {
+            // The next |step| blocks are optimized out.
+            is_optimized = true;
+        }
+
+        // Advance offsets by |step|, and go to the next non-empty extent if the current extent is
+        // depleted.
+        s_offset += step;
+        d_offset += step;
+        while (s_it != src_extents.end() && s_offset >= s_it->num_blocks()) {
+            ++s_it;
+            s_offset = 0;
+        }
+        while (d_it != dst_extents.end() && d_offset >= d_it->num_blocks()) {
+            ++d_it;
+            d_offset = 0;
+        }
+    }
+    return is_optimized;
 }
 
 void WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Extent& de,
@@ -101,12 +152,15 @@
     if (operations == nullptr) return sc.cow_size_bytes();
 
     for (const auto& iop : *operations) {
-        // Do not allocate space for operations that are going to be skipped
+        const InstallOperation* written_op = &iop;
+        InstallOperation buf;
+        // Do not allocate space for extents that are going to be skipped
         // during OTA application.
-        if (iop.type() == InstallOperation::SOURCE_COPY && SourceCopyOperationIsClone(iop))
-            continue;
+        if (iop.type() == InstallOperation::SOURCE_COPY && OptimizeSourceCopyOperation(iop, &buf)) {
+            written_op = &buf;
+        }
 
-        for (const auto& de : iop.dst_extents()) {
+        for (const auto& de : written_op->dst_extents()) {
             WriteExtent(&sc, de, sectors_per_block);
         }
     }
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index 9da3f05..526f874 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -12,6 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <optional>
+#include <tuple>
+
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <libdm/dm.h>
@@ -26,6 +29,13 @@
 
 using namespace android::fs_mgr;
 
+using chromeos_update_engine::InstallOperation;
+using UeExtent = chromeos_update_engine::Extent;
+using google::protobuf::RepeatedPtrField;
+using ::testing::Matches;
+using ::testing::Pointwise;
+using ::testing::Truly;
+
 namespace android {
 namespace snapshot {
 
@@ -213,5 +223,76 @@
     }
 }
 
+void BlocksToExtents(const std::vector<uint64_t>& blocks,
+                     google::protobuf::RepeatedPtrField<UeExtent>* extents) {
+    for (uint64_t block : blocks) {
+        AppendExtent(extents, block, 1);
+    }
+}
+
+template <typename T>
+std::vector<uint64_t> ExtentsToBlocks(const T& extents) {
+    std::vector<uint64_t> blocks;
+    for (const auto& extent : extents) {
+        for (uint64_t offset = 0; offset < extent.num_blocks(); ++offset) {
+            blocks.push_back(extent.start_block() + offset);
+        }
+    }
+    return blocks;
+}
+
+InstallOperation CreateCopyOp(const std::vector<uint64_t>& src_blocks,
+                              const std::vector<uint64_t>& dst_blocks) {
+    InstallOperation op;
+    op.set_type(InstallOperation::SOURCE_COPY);
+    BlocksToExtents(src_blocks, op.mutable_src_extents());
+    BlocksToExtents(dst_blocks, op.mutable_dst_extents());
+    return op;
+}
+
+// ExtentEqual(tuple<UeExtent, UeExtent>)
+MATCHER(ExtentEqual, "") {
+    auto&& [a, b] = arg;
+    return a.start_block() == b.start_block() && a.num_blocks() == b.num_blocks();
+}
+
+struct OptimizeOperationTestParam {
+    InstallOperation input;
+    std::optional<InstallOperation> expected_output;
+};
+
+class OptimizeOperationTest : public ::testing::TestWithParam<OptimizeOperationTestParam> {};
+TEST_P(OptimizeOperationTest, Test) {
+    InstallOperation actual_output;
+    EXPECT_EQ(GetParam().expected_output.has_value(),
+              OptimizeSourceCopyOperation(GetParam().input, &actual_output))
+            << "OptimizeSourceCopyOperation should "
+            << (GetParam().expected_output.has_value() ? "succeed" : "fail");
+    if (!GetParam().expected_output.has_value()) return;
+    EXPECT_THAT(actual_output.src_extents(),
+                Pointwise(ExtentEqual(), GetParam().expected_output->src_extents()));
+    EXPECT_THAT(actual_output.dst_extents(),
+                Pointwise(ExtentEqual(), GetParam().expected_output->dst_extents()));
+}
+
+std::vector<OptimizeOperationTestParam> GetOptimizeOperationTestParams() {
+    return {
+            {CreateCopyOp({}, {}), CreateCopyOp({}, {})},
+            {CreateCopyOp({1, 2, 4}, {1, 2, 4}), CreateCopyOp({}, {})},
+            {CreateCopyOp({1, 2, 3}, {4, 5, 6}), std::nullopt},
+            {CreateCopyOp({3, 2}, {1, 2}), CreateCopyOp({3}, {1})},
+            {CreateCopyOp({5, 6, 3, 4, 1, 2}, {1, 2, 3, 4, 5, 6}),
+             CreateCopyOp({5, 6, 1, 2}, {1, 2, 5, 6})},
+            {CreateCopyOp({1, 2, 3, 5, 5, 6}, {5, 6, 3, 4, 1, 2}),
+             CreateCopyOp({1, 2, 5, 5, 6}, {5, 6, 4, 1, 2})},
+            {CreateCopyOp({1, 2, 5, 6, 9, 10}, {1, 4, 5, 6, 7, 8}),
+             CreateCopyOp({2, 9, 10}, {4, 7, 8})},
+            {CreateCopyOp({2, 3, 3, 4, 4}, {1, 2, 3, 4, 5}), CreateCopyOp({2, 3, 4}, {1, 2, 5})},
+    };
+}
+
+INSTANTIATE_TEST_CASE_P(Snapshot, OptimizeOperationTest,
+                        ::testing::ValuesIn(GetOptimizeOperationTestParams()));
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 61fc2df..7e84c48 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -21,7 +21,6 @@
 #include <sys/unistd.h>
 
 #include <optional>
-#include <sstream>
 #include <thread>
 #include <unordered_set>
 
@@ -38,10 +37,6 @@
 #include <libfiemap/image_manager.h>
 #include <liblp/liblp.h>
 
-#ifdef LIBSNAPSHOT_USE_CALLSTACK
-#include <utils/CallStack.h>
-#endif
-
 #include <android/snapshot/snapshot.pb.h>
 #include <libsnapshot/snapshot_stats.h>
 #include "device_info.h"
@@ -81,6 +76,7 @@
 using namespace std::string_literals;
 
 static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
+static constexpr char kRollbackIndicatorPath[] = "/metadata/ota/rollback-indicator";
 static constexpr auto kUpdateStateCheckInterval = 2s;
 
 // Note: IImageManager is an incomplete type in the header, so the default
@@ -227,25 +223,6 @@
 
     LOG(INFO) << "Removing all update state.";
 
-#ifdef LIBSNAPSHOT_USE_CALLSTACK
-    LOG(WARNING) << "Logging stack; see b/148818798.";
-    // Do not use CallStack's log functions because snapshotctl relies on
-    // android-base/logging to save log to files.
-    // TODO(b/148818798): remove this before we ship.
-    CallStack callstack;
-    callstack.update();
-    auto callstack_str = callstack.toString();
-    LOG(WARNING) << callstack_str.c_str();
-    std::stringstream path;
-    path << "/data/misc/snapshotctl_log/libsnapshot." << Now() << ".log";
-    std::string path_str = path.str();
-    android::base::WriteStringToFile(callstack_str.c_str(), path_str);
-    if (chmod(path_str.c_str(), 0644) == -1) {
-        PLOG(WARNING) << "Unable to chmod 0644 "
-                      << ", file maybe dropped from bugreport:" << path_str;
-    }
-#endif
-
     if (!RemoveAllSnapshots(lock)) {
         LOG(ERROR) << "Could not remove all snapshots";
         return false;
@@ -1012,7 +989,7 @@
 }
 
 std::string SnapshotManager::GetRollbackIndicatorPath() {
-    return metadata_dir_ + "/rollback-indicator";
+    return metadata_dir_ + "/" + android::base::Basename(kRollbackIndicatorPath);
 }
 
 void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
@@ -1469,6 +1446,10 @@
     return access(kBootIndicatorPath, F_OK) == 0;
 }
 
+std::string SnapshotManager::GetGlobalRollbackIndicatorPath() {
+    return kRollbackIndicatorPath;
+}
+
 bool SnapshotManager::NeedSnapshotsInFirstStageMount() {
     // If we fail to read, we'll wind up using CreateLogicalPartitions, which
     // will create devices that look like the old slot, except with extra
@@ -2490,68 +2471,6 @@
     return AutoUnmountDevice::New(device_->GetMetadataDir());
 }
 
-UpdateState SnapshotManager::InitiateMergeAndWait(SnapshotMergeReport* stats_report,
-                                                  const std::function<bool()>& before_cancel) {
-    {
-        auto lock = LockExclusive();
-        // Sync update state from file with bootloader.
-        if (!WriteUpdateState(lock.get(), ReadUpdateState(lock.get()))) {
-            LOG(WARNING) << "Unable to sync write update state, fastboot may "
-                         << "reject / accept wipes incorrectly!";
-        }
-    }
-
-    auto merge_stats = SnapshotMergeStats::GetInstance(*this);
-
-    unsigned int last_progress = 0;
-    auto callback = [&]() -> bool {
-        double progress;
-        GetUpdateState(&progress);
-        if (last_progress < static_cast<unsigned int>(progress)) {
-            last_progress = progress;
-            LOG(INFO) << "Waiting for merge to complete: " << last_progress << "%.";
-        }
-        return true;  // continue
-    };
-
-    LOG(INFO) << "Waiting for any previous merge request to complete. "
-              << "This can take up to several minutes.";
-    merge_stats->Start();
-    auto state = ProcessUpdateState(callback, before_cancel);
-    merge_stats->set_state(state);
-    if (state == UpdateState::None) {
-        LOG(INFO) << "Can't find any snapshot to merge.";
-        return state;
-    }
-    if (state == UpdateState::Unverified) {
-        if (GetCurrentSlot() != Slot::Target) {
-            LOG(INFO) << "Cannot merge until device reboots.";
-            return state;
-        }
-
-        if (!InitiateMerge()) {
-            LOG(ERROR) << "Failed to initiate merge.";
-            return state;
-        }
-        // All other states can be handled by ProcessUpdateState.
-        LOG(INFO) << "Waiting for merge to complete. This can take up to several minutes.";
-        last_progress = 0;
-        state = ProcessUpdateState(callback, before_cancel);
-        merge_stats->set_state(state);
-    }
-
-    LOG(INFO) << "Merge finished with state \"" << state << "\".";
-    if (stats_report) {
-        auto result = merge_stats->Finish();
-        if (result) {
-            *stats_report = result->report();
-        } else {
-            LOG(WARNING) << "SnapshotMergeStatus::Finish failed.";
-        }
-    }
-    return state;
-}
-
 bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callback) {
     if (!device_->IsRecovery()) {
         LOG(ERROR) << "Data wipes are only allowed in recovery.";
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 7d16ec2..855451d 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -1027,7 +1027,8 @@
     }
 
     // Initiate the merge and wait for it to be completed.
-    ASSERT_EQ(UpdateState::MergeCompleted, init->InitiateMergeAndWait());
+    ASSERT_TRUE(init->InitiateMerge());
+    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
 
     // Check that the target partitions have the same content after the merge.
     for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
@@ -1201,7 +1202,8 @@
 
     // Initiate the merge and wait for it to be completed.
     auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
-    ASSERT_EQ(UpdateState::MergeCompleted, new_sm->InitiateMergeAndWait());
+    ASSERT_TRUE(new_sm->InitiateMerge());
+    ASSERT_EQ(UpdateState::MergeCompleted, new_sm->ProcessUpdateState());
 
     // Execute the second update.
     ASSERT_TRUE(new_sm->BeginUpdate());
@@ -1341,7 +1343,8 @@
     ASSERT_GE(fd, 0);
 
     // COW cannot be removed due to open fd, so expect a soft failure.
-    ASSERT_EQ(UpdateState::MergeNeedsReboot, init->InitiateMergeAndWait());
+    ASSERT_TRUE(init->InitiateMerge());
+    ASSERT_EQ(UpdateState::MergeNeedsReboot, init->ProcessUpdateState());
 
     // Simulate shutting down the device.
     fd.reset();
@@ -1354,7 +1357,7 @@
     ASSERT_FALSE(sm->IsSnapshotDevice("sys_b", nullptr));
 
     // Merge should be able to complete now.
-    ASSERT_EQ(UpdateState::MergeCompleted, init->InitiateMergeAndWait());
+    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
 }
 
 class MetadataMountedTest : public SnapshotUpdateTest {
@@ -1691,7 +1694,8 @@
 
     // There should be no snapshot to merge.
     auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, flashed_slot_suffix));
-    ASSERT_EQ(UpdateState::Cancelled, new_sm->InitiateMergeAndWait());
+    // update_enigne calls ProcessUpdateState first -- should see Cancelled.
+    ASSERT_EQ(UpdateState::Cancelled, new_sm->ProcessUpdateState());
 
     // Next OTA calls CancelUpdate no matter what.
     ASSERT_TRUE(new_sm->CancelUpdate());
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index aa5e9c1..a44de84 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -24,12 +24,8 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
-#include <android/snapshot/snapshot.pb.h>
-#include <libsnapshot/snapshot.h>
-#include <libsnapshot/snapshot_stats.h>
-#include <statslog.h>
 
-#include "utility.h"
+#include <libsnapshot/snapshot.h>
 
 using namespace std::string_literals;
 
@@ -39,146 +35,22 @@
                  "Actions:\n"
                  "  dump\n"
                  "    Print snapshot states.\n"
-                 "  merge [--logcat] [--log-to-file] [--report] [--dry-run]\n"
-                 "    Initialize merge and wait for it to be completed.\n"
-                 "    If --logcat is specified, log to logcat.\n"
-                 "    If --log-to-file is specified, log to /data/misc/snapshotctl_log/.\n"
-                 "    If both specified, log to both. If none specified, log to stdout.\n"
-                 "    If --report is specified, send merge statistics to statsd.\n"
-                 "    If --dry-run flag, no real merge operation is is triggered, and\n"
-                 "      sample statistics are sent to statsd for testing purpose.\n";
+                 "  merge\n"
+                 "    Deprecated.\n";
     return EX_USAGE;
 }
 
 namespace android {
 namespace snapshot {
 
-static SnapshotMergeReport GetDummySnapshotMergeReport() {
-    SnapshotMergeReport fake_report;
-
-    fake_report.set_state(UpdateState::MergeCompleted);
-    fake_report.set_resume_count(56);
-
-    return fake_report;
-}
-
 bool DumpCmdHandler(int /*argc*/, char** argv) {
     android::base::InitLogging(argv, &android::base::StderrLogger);
     return SnapshotManager::New()->Dump(std::cout);
 }
 
-class FileLogger {
-  public:
-    FileLogger() {
-        static constexpr const char* kLogFilePath = "/data/misc/snapshotctl_log/";
-        std::stringstream ss;
-        ss << kLogFilePath << "snapshotctl." << Now() << ".log";
-        fd_.reset(TEMP_FAILURE_RETRY(
-                open(ss.str().c_str(),
-                     O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_SYNC, 0644)));
-        if (fd_ == -1) {
-            PLOG(ERROR) << "Cannot open persistent log " << ss.str();
-            return;
-        }
-        // Explicitly chmod again because mode in open() may be masked by umask.
-        if (fchmod(fd_.get(), 0644) == -1) {
-            PLOG(ERROR) << "Cannot chmod 0644 persistent log " << ss.str();
-            return;
-        }
-    }
-    // Copy-contuctor needed to be converted to std::function.
-    FileLogger(const FileLogger& other) { fd_.reset(dup(other.fd_)); }
-    void operator()(android::base::LogId, android::base::LogSeverity, const char* /*tag*/,
-                    const char* /*file*/, unsigned int /*line*/, const char* message) {
-        if (fd_ == -1) return;
-        std::stringstream ss;
-        ss << Now() << ":" << message << "\n";
-        (void)android::base::WriteStringToFd(ss.str(), fd_);
-    }
-
-  private:
-    android::base::unique_fd fd_;
-};
-
-class MergeCmdLogger {
-  public:
-    MergeCmdLogger(int argc, char** argv) {
-        for (int i = 0; i < argc; ++i) {
-            if (argv[i] == "--logcat"s) {
-                loggers_.push_back(android::base::LogdLogger());
-            }
-            if (argv[i] == "--log-to-file"s) {
-                loggers_.push_back(std::move(FileLogger()));
-            }
-        }
-        if (loggers_.empty()) {
-            loggers_.push_back(&android::base::StdioLogger);
-        }
-    }
-    void operator()(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
-                    const char* file, unsigned int line, const char* message) {
-        for (auto&& logger : loggers_) {
-            logger(id, severity, tag, file, line, message);
-        }
-    }
-
-  private:
-    std::vector<android::base::LogFunction> loggers_;
-};
-
-bool MergeCmdHandler(int argc, char** argv) {
-    std::chrono::milliseconds passed_ms;
-
-    bool report_to_statsd = false;
-    bool dry_run = false;
-    for (int i = 2; i < argc; ++i) {
-        if (argv[i] == "--report"s) {
-            report_to_statsd = true;
-        } else if (argv[i] == "--dry-run"s) {
-            dry_run = true;
-        }
-    }
-
-    // 'snapshotctl merge' is stripped away from arguments to
-    // Logger.
-    android::base::InitLogging(argv);
-    android::base::SetLogger(MergeCmdLogger(argc - 2, argv + 2));
-
-    UpdateState state;
-    SnapshotMergeReport merge_report;
-    if (dry_run) {
-        merge_report = GetDummySnapshotMergeReport();
-        state = merge_report.state();
-        passed_ms = std::chrono::milliseconds(1234);
-    } else {
-        auto begin = std::chrono::steady_clock::now();
-
-        state = SnapshotManager::New()->InitiateMergeAndWait(&merge_report);
-
-        // We could wind up in the Unverified state if the device rolled back or
-        // hasn't fully rebooted. Ignore this.
-        if (state == UpdateState::None || state == UpdateState::Unverified) {
-            return true;
-        }
-
-        auto end = std::chrono::steady_clock::now();
-        passed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin);
-    }
-
-    if (report_to_statsd) {
-        android::util::stats_write(android::util::SNAPSHOT_MERGE_REPORTED,
-                                   static_cast<int32_t>(merge_report.state()),
-                                   static_cast<int64_t>(passed_ms.count()),
-                                   static_cast<int32_t>(merge_report.resume_count()));
-    }
-
-    if (state == UpdateState::MergeCompleted) {
-        LOG(INFO) << "Snapshot merged in " << passed_ms.count() << " ms.";
-        return true;
-    }
-
-    LOG(ERROR) << "Snapshot failed to merge with state \"" << state << "\".";
-
+bool MergeCmdHandler(int /*argc*/, char** argv) {
+    android::base::InitLogging(argv, &android::base::StderrLogger);
+    LOG(WARNING) << "Deprecated. Call update_engine_client --merge instead.";
     return false;
 }
 
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 3318b33..d32b61e 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -34,6 +34,7 @@
 using android::fs_mgr::MetadataBuilder;
 using android::fs_mgr::Partition;
 using android::fs_mgr::ReadDefaultFstab;
+using google::protobuf::RepeatedPtrField;
 
 namespace android {
 namespace snapshot {
@@ -166,5 +167,20 @@
     return os << std::put_time(&now, "%Y%m%d-%H%M%S");
 }
 
+void AppendExtent(RepeatedPtrField<chromeos_update_engine::Extent>* extents, uint64_t start_block,
+                  uint64_t num_blocks) {
+    if (extents->size() > 0) {
+        auto last_extent = extents->rbegin();
+        auto next_block = last_extent->start_block() + last_extent->num_blocks();
+        if (start_block == next_block) {
+            last_extent->set_num_blocks(last_extent->num_blocks() + num_blocks);
+            return;
+        }
+    }
+    auto* new_extent = extents->Add();
+    new_extent->set_start_block(start_block);
+    new_extent->set_num_blocks(num_blocks);
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 90ad0fe..e69bdad 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -125,5 +125,9 @@
 struct Now {};
 std::ostream& operator<<(std::ostream& os, const Now&);
 
+// Append to |extents|. Merged into the last element if possible.
+void AppendExtent(google::protobuf::RepeatedPtrField<chromeos_update_engine::Extent>* extents,
+                  uint64_t start_block, uint64_t num_blocks);
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 9caae35..16e38f1 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -27,6 +27,7 @@
 #include <android-base/file.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
+#include <fs_mgr.h>
 #include <fstab/fstab.h>
 #include <gtest/gtest.h>
 
@@ -1001,6 +1002,10 @@
     }
     Fstab fstab;
     ASSERT_TRUE(ReadDefaultFstab(&fstab)) << "Failed to read default fstab";
-    ASSERT_NE(nullptr, GetMountedEntryForUserdata(&fstab))
+    Fstab proc_mounts;
+    ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &proc_mounts)) << "Failed to read /proc/mounts";
+    auto mounted_entry = GetEntryForMountPoint(&proc_mounts, "/data");
+    ASSERT_NE(mounted_entry, nullptr) << "/data is not mounted";
+    ASSERT_NE(nullptr, fs_mgr_get_mounted_entry_for_userdata(&fstab, *mounted_entry))
             << "/data wasn't mounted from default fstab";
 }
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 2175075..aa36849 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -323,20 +323,10 @@
     }
     if (bootstrap_ns_id != GetMountNamespaceId() && bootstrap_ns_fd.get() != -1 &&
         IsApexUpdatable()) {
-        // The property service thread and its descendent threads must be in the correct mount
-        // namespace to call Service::Start(), however setns() only operates on a single thread and
-        // fails when secondary threads attempt to join the same mount namespace.  Therefore, we
-        // must join the property service thread and its descendents before the setns() call.  Those
-        // threads are then started again after the setns() call, and they'll be in the proper
-        // namespace.
-        PausePropertyService();
-
         if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
             PLOG(ERROR) << "Failed to switch to bootstrap mount namespace.";
             return false;
         }
-
-        ResumePropertyService();
     }
     return true;
 }
diff --git a/init/selinux.cpp b/init/selinux.cpp
index c5b7576..acbcbd6 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -66,6 +66,7 @@
 #include <android-base/unique_fd.h>
 #include <fs_avb/fs_avb.h>
 #include <libgsi/libgsi.h>
+#include <libsnapshot/snapshot.h>
 #include <selinux/android.h>
 
 #include "debug_ramdisk.h"
@@ -78,6 +79,7 @@
 using android::base::Timer;
 using android::base::unique_fd;
 using android::fs_mgr::AvbHandle;
+using android::snapshot::SnapshotManager;
 
 namespace android {
 namespace init {
@@ -535,7 +537,11 @@
 
     selinux_android_restorecon("/linkerconfig", 0);
 
-    selinux_android_restorecon(gsi::kDsuAvbKeyDir, SELINUX_ANDROID_RESTORECON_RECURSE);
+    // adb remount, snapshot-based updates, and DSUs all create files during
+    // first-stage init.
+    selinux_android_restorecon("/metadata", SELINUX_ANDROID_RESTORECON_RECURSE);
+
+    selinux_android_restorecon(SnapshotManager::GetGlobalRollbackIndicatorPath().c_str(), 0);
 }
 
 int SelinuxKlogCallback(int type, const char* fmt, ...) {
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 565f2c3..59bd97c 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -89,6 +89,9 @@
         },
         android: {
             static_libs: ["libasync_safe"],
+            static: {
+                whole_static_libs: ["libasync_safe"],
+            },
         },
         vendor: {
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 4e93df3..fc06c1d 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -45,13 +45,9 @@
 }
 
 // Socket specific parts of libcutils that are safe to statically link into an APEX.
-cc_library_static {
+cc_library {
     name: "libcutils_sockets",
     vendor_available: true,
-    vndk: {
-        enabled: true,
-        support_system_process: true,
-    },
     recovery_available: true,
     host_supported: true,
     native_bridge_supported: true,
@@ -62,6 +58,7 @@
 
     export_include_dirs: ["include"],
 
+    shared_libs: ["liblog"],
     srcs: ["sockets.cpp"],
     target: {
         linux_bionic: {
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index c4e4f85..5805a4d 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -201,6 +201,8 @@
                                            CAP_MASK_LONG(CAP_SETGID),
                                               "system/bin/simpleperf_app_runner" },
     { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/e2fsck" },
+    { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/tune2fs" },
+    { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/resize2fs" },
     // generic defaults
     { 00755, AID_ROOT,      AID_ROOT,      0, "bin/*" },
     { 00640, AID_ROOT,      AID_SHELL,     0, "fstab.*" },
diff --git a/liblog/Android.bp b/liblog/Android.bp
index f1e5118..8410370 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -117,8 +117,12 @@
     logtags: ["event.logtags"],
     compile_multilib: "both",
     apex_available: [
-        "//apex_available:anyapex",
         "//apex_available:platform",
+        // liblog is exceptionally available to the runtime APEX
+        // because the dynamic linker has to use it statically.
+        // See b/151051671
+        "com.android.runtime",
+        // DO NOT add more apex names here
     ],
 }
 
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index c98455d..43a91ab 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -56,6 +56,12 @@
 
 #include <stdarg.h>
 #include <stddef.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+#if !defined(__BIONIC__) && !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(x)
+#endif
 
 #ifdef __cplusplus
 extern "C" {
@@ -149,14 +155,11 @@
   /** The kernel log buffer. */
   LOG_ID_KERNEL = 7,
 
-  LOG_ID_MAX
-} log_id_t;
+  LOG_ID_MAX,
 
-/**
- * Let the logging function choose the best log target.
- * This is not part of the enum since adding either -1 or 0xFFFFFFFF forces the enum to be signed or
- * unsigned, which breaks unfortunately common arithmetic against LOG_ID_MIN and LOG_ID_MAX. */
-#define LOG_ID_DEFAULT -1
+  /** Let the logging function choose the best log target. */
+  LOG_ID_DEFAULT = 0x7FFFFFFF
+} log_id_t;
 
 /**
  * Writes the constant string `text` to the log buffer `id`,
@@ -183,14 +186,26 @@
  */
 struct __android_logger_data {
   size_t struct_size; /* Must be set to sizeof(__android_logger_data) and is used for versioning. */
-  int buffer_id;      /* log_id_t or -1 to represent 'default'. */
-  int priority;       /* android_LogPriority values. */
+  int32_t buffer_id;  /* log_id_t or -1 to represent 'default'. */
+  int32_t priority;   /* android_LogPriority values. */
   const char* tag;
   const char* file;  /* Optional file name, may be set to nullptr. */
-  unsigned int line; /* Optional line number, ignore if file is nullptr. */
+  uint32_t line;     /* Optional line number, ignore if file is nullptr. */
 };
 
 /**
+ * Prototype for the 'logger' function that is called for every log message.
+ */
+typedef void (*__android_logger_function)(const struct __android_logger_data* logger_data,
+                                          const char* message);
+/**
+ * Prototype for the 'abort' function that is called when liblog will abort due to
+ * __android_log_assert() failures.
+ */
+typedef void (*__android_aborter_function)(const char* abort_message);
+
+#if !defined(__ANDROID__) || __ANDROID_API__ >= 30
+/**
  * Writes the log message specified with logger_data and msg to the log.  logger_data includes
  * additional file name and line number information that a logger may use.  logger_data is versioned
  * for backwards compatibility.
@@ -199,54 +214,44 @@
  * buffers, then pass the message to liblog via this function, and therefore we do not want to
  * duplicate the loggability check here.
  */
-void __android_log_write_logger_data(struct __android_logger_data* logger_data, const char* msg);
-
-/**
- * Prototype for the 'logger' function that is called for every log message.
- */
-typedef void (*__android_logger_function)(const struct __android_logger_data* logger_data,
-                                          const char* message);
+void __android_log_write_logger_data(struct __android_logger_data* logger_data, const char* msg)
+    __INTRODUCED_IN(30);
 
 /**
  * Sets a user defined logger function.  All log messages sent to liblog will be set to the
  * function pointer specified by logger for processing.
  */
-void __android_log_set_logger(__android_logger_function logger);
+void __android_log_set_logger(__android_logger_function logger) __INTRODUCED_IN(30);
 
 /**
  * Writes the log message to logd.  This is an __android_logger_function and can be provided to
  * __android_log_set_logger().  It is the default logger when running liblog on a device.
  */
-void __android_log_logd_logger(const struct __android_logger_data* logger_data, const char* msg);
+void __android_log_logd_logger(const struct __android_logger_data* logger_data, const char* msg)
+    __INTRODUCED_IN(30);
 
 /**
  * Writes the log message to stderr.  This is an __android_logger_function and can be provided to
  * __android_log_set_logger().  It is the default logger when running liblog on host.
  */
 void __android_log_stderr_logger(const struct __android_logger_data* logger_data,
-                                 const char* message);
-
-/**
- * Prototype for the 'abort' function that is called when liblog will abort due to
- * __android_log_assert() failures.
- */
-typedef void (*__android_aborter_function)(const char* abort_message);
+                                 const char* message) __INTRODUCED_IN(30);
 
 /**
  * Sets a user defined aborter function that is called for __android_log_assert() failures.
  */
-void __android_log_set_aborter(__android_aborter_function aborter);
+void __android_log_set_aborter(__android_aborter_function aborter) __INTRODUCED_IN(30);
 
 /**
  * Calls the stored aborter function.  This allows for other logging libraries to use the same
  * aborter function by calling this function in liblog.
  */
-void __android_log_call_aborter(const char* abort_message);
+void __android_log_call_aborter(const char* abort_message) __INTRODUCED_IN(30);
 
 /**
  * Sets android_set_abort_message() on device then aborts().  This is the default aborter.
  */
-void __android_log_default_aborter(const char* abort_message);
+void __android_log_default_aborter(const char* abort_message) __INTRODUCED_IN(30);
 
 /**
  * Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from
@@ -260,28 +265,30 @@
  *
  * prio is ANDROID_LOG_VERBOSE to ANDROID_LOG_FATAL.
  */
-int __android_log_is_loggable(int prio, const char* tag, int default_prio);
-int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio);
+int __android_log_is_loggable(int prio, const char* tag, int default_prio) __INTRODUCED_IN(30);
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio)
+    __INTRODUCED_IN(30);
 
 /**
  * Sets the minimum priority that will be logged for this process.
  *
  * This returns the previous set minimum priority, or ANDROID_LOG_DEFAULT if none was set.
  */
-int __android_log_set_minimum_priority(int priority);
+int32_t __android_log_set_minimum_priority(int32_t priority) __INTRODUCED_IN(30);
 
 /**
  * Gets the minimum priority that will be logged for this process.  If none has been set by a
  * previous __android_log_set_minimum_priority() call, this returns ANDROID_LOG_DEFAULT.
  */
-int __android_log_get_minimum_priority(void);
+int32_t __android_log_get_minimum_priority(void) __INTRODUCED_IN(30);
 
 /**
  * Sets the default tag if no tag is provided when writing a log message.  Defaults to
  * getprogname().  This truncates tag to the maximum log message size, though appropriate tags
  * should be much smaller.
  */
-void __android_log_set_default_tag(const char* tag);
+void __android_log_set_default_tag(const char* tag) __INTRODUCED_IN(30);
+#endif
 
 #ifdef __cplusplus
 }
diff --git a/liblog/logger_name.cpp b/liblog/logger_name.cpp
index 7d676f4..e72290e 100644
--- a/liblog/logger_name.cpp
+++ b/liblog/logger_name.cpp
@@ -41,7 +41,10 @@
 }
 
 static_assert(std::is_same<std::underlying_type<log_id_t>::type, uint32_t>::value,
-              "log_id_t must be an unsigned int");
+              "log_id_t must be an uint32_t");
+
+static_assert(std::is_same<std::underlying_type<android_LogPriority>::type, uint32_t>::value,
+              "log_id_t must be an uint32_t");
 
 log_id_t android_name_to_log_id(const char* logName) {
   const char* b;
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index cf82e0f..b420fa0 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -27,6 +27,7 @@
 #include <android/set_abort_message.h>
 #endif
 
+#include <atomic>
 #include <shared_mutex>
 
 #include <android-base/errno_restorer.h>
@@ -148,14 +149,12 @@
   GetDefaultTag().assign(tag, 0, LOGGER_ENTRY_MAX_PAYLOAD);
 }
 
-static int minimum_log_priority = ANDROID_LOG_DEFAULT;
-int __android_log_set_minimum_priority(int priority) {
-  int old_minimum_log_priority = minimum_log_priority;
-  minimum_log_priority = priority;
-  return old_minimum_log_priority;
+static std::atomic_int32_t minimum_log_priority = ANDROID_LOG_DEFAULT;
+int32_t __android_log_set_minimum_priority(int32_t priority) {
+  return minimum_log_priority.exchange(priority, std::memory_order_relaxed);
 }
 
-int __android_log_get_minimum_priority() {
+int32_t __android_log_get_minimum_priority() {
   return minimum_log_priority;
 }
 
@@ -268,7 +267,7 @@
   static const char log_characters[] = "XXVDIWEF";
   static_assert(arraysize(log_characters) - 1 == ANDROID_LOG_SILENT,
                 "Mismatch in size of log_characters and values in android_LogPriority");
-  int priority =
+  int32_t priority =
       logger_data->priority > ANDROID_LOG_SILENT ? ANDROID_LOG_FATAL : logger_data->priority;
   char priority_char = log_characters[priority];
   uint64_t tid = GetThreadId();
diff --git a/libstats/push_compat/Android.bp b/libstats/push_compat/Android.bp
index 3e69556..f0fcff6 100644
--- a/libstats/push_compat/Android.bp
+++ b/libstats/push_compat/Android.bp
@@ -35,8 +35,9 @@
     header_libs: ["libstatssocket_headers"],
     static_libs: [
         "libbase",
+    ],
+    shared_libs: [
         "liblog",
-        "libutils",
     ],
 }
 
diff --git a/libstats/push_compat/StatsEventCompat.cpp b/libstats/push_compat/StatsEventCompat.cpp
index de458b3..e1a86ae 100644
--- a/libstats/push_compat/StatsEventCompat.cpp
+++ b/libstats/push_compat/StatsEventCompat.cpp
@@ -15,12 +15,16 @@
  */
 
 #include "include/StatsEventCompat.h"
+
+#include <chrono>
+
+#include <android-base/chrono_utils.h>
 #include <android-base/properties.h>
 #include <android/api-level.h>
 #include <android/log.h>
 #include <dlfcn.h>
-#include <utils/SystemClock.h>
 
+using android::base::boot_clock;
 using android::base::GetProperty;
 
 const static int kStatsEventTag = 1937006964;
@@ -36,21 +40,26 @@
         GetProperty("ro.build.version.codename", "") == "R" ||
         android_get_device_api_level() > __ANDROID_API_Q__;
 
-// definitions of static class variables
+// initializations of static class variables
 bool StatsEventCompat::mAttemptedLoad = false;
-void* StatsEventCompat::mStatsEventApi = nullptr;
 std::mutex StatsEventCompat::mLoadLock;
+AStatsEventApi StatsEventCompat::mAStatsEventApi;
+
+static int64_t elapsedRealtimeNano() {
+    return std::chrono::time_point_cast<std::chrono::nanoseconds>(boot_clock::now())
+            .time_since_epoch()
+            .count();
+}
 
 StatsEventCompat::StatsEventCompat() : mEventQ(kStatsEventTag) {
     // guard loading because StatsEventCompat might be called from multithreaded
     // environment
     {
         std::lock_guard<std::mutex> lg(mLoadLock);
-        if (!mAttemptedLoad) {
+        if (!mAttemptedLoad && mPlatformAtLeastR) {
             void* handle = dlopen("libstatssocket.so", RTLD_NOW);
             if (handle) {
-                //                mStatsEventApi = (struct AStatsEvent_apiTable*)dlsym(handle,
-                //                "table");
+                initializeApiTableLocked(handle);
             } else {
                 ALOGE("dlopen failed: %s\n", dlerror());
             }
@@ -58,61 +67,93 @@
         mAttemptedLoad = true;
     }
 
-    if (mStatsEventApi) {
-        //        mEventR = mStatsEventApi->obtain();
-    } else if (!mPlatformAtLeastR) {
-        mEventQ << android::elapsedRealtimeNano();
+    if (useRSchema()) {
+        mEventR = mAStatsEventApi.obtain();
+    } else if (useQSchema()) {
+        mEventQ << elapsedRealtimeNano();
     }
 }
 
 StatsEventCompat::~StatsEventCompat() {
-    //    if (mStatsEventApi) mStatsEventApi->release(mEventR);
+    if (useRSchema()) mAStatsEventApi.release(mEventR);
+}
+
+// Populates the AStatsEventApi struct by calling dlsym to find the address of
+// each API function.
+void StatsEventCompat::initializeApiTableLocked(void* handle) {
+    mAStatsEventApi.obtain = (AStatsEvent* (*)())dlsym(handle, "AStatsEvent_obtain");
+    mAStatsEventApi.build = (void (*)(AStatsEvent*))dlsym(handle, "AStatsEvent_build");
+    mAStatsEventApi.write = (int (*)(AStatsEvent*))dlsym(handle, "AStatsEvent_write");
+    mAStatsEventApi.release = (void (*)(AStatsEvent*))dlsym(handle, "AStatsEvent_release");
+    mAStatsEventApi.setAtomId =
+            (void (*)(AStatsEvent*, uint32_t))dlsym(handle, "AStatsEvent_setAtomId");
+    mAStatsEventApi.writeInt32 =
+            (void (*)(AStatsEvent*, int32_t))dlsym(handle, "AStatsEvent_writeInt32");
+    mAStatsEventApi.writeInt64 =
+            (void (*)(AStatsEvent*, int64_t))dlsym(handle, "AStatsEvent_writeInt64");
+    mAStatsEventApi.writeFloat =
+            (void (*)(AStatsEvent*, float))dlsym(handle, "AStatsEvent_writeFloat");
+    mAStatsEventApi.writeBool =
+            (void (*)(AStatsEvent*, bool))dlsym(handle, "AStatsEvent_writeBool");
+    mAStatsEventApi.writeByteArray = (void (*)(AStatsEvent*, const uint8_t*, size_t))dlsym(
+            handle, "AStatsEvent_writeByteArray");
+    mAStatsEventApi.writeString =
+            (void (*)(AStatsEvent*, const char*))dlsym(handle, "AStatsEvent_writeString");
+    mAStatsEventApi.writeAttributionChain =
+            (void (*)(AStatsEvent*, const uint32_t*, const char* const*, uint8_t))dlsym(
+                    handle, "AStatsEvent_writeAttributionChain");
+    mAStatsEventApi.addBoolAnnotation =
+            (void (*)(AStatsEvent*, uint8_t, bool))dlsym(handle, "AStatsEvent_addBoolAnnotation");
+    mAStatsEventApi.addInt32Annotation = (void (*)(AStatsEvent*, uint8_t, int32_t))dlsym(
+            handle, "AStatsEvent_addInt32Annotation");
+
+    mAStatsEventApi.initialized = true;
 }
 
 void StatsEventCompat::setAtomId(int32_t atomId) {
-    if (mStatsEventApi) {
-        //        mStatsEventApi->setAtomId(mEventR, (uint32_t)atomId);
-    } else if (!mPlatformAtLeastR) {
+    if (useRSchema()) {
+        mAStatsEventApi.setAtomId(mEventR, (uint32_t)atomId);
+    } else if (useQSchema()) {
         mEventQ << atomId;
     }
 }
 
 void StatsEventCompat::writeInt32(int32_t value) {
-    if (mStatsEventApi) {
-        //        mStatsEventApi->writeInt32(mEventR, value);
-    } else if (!mPlatformAtLeastR) {
+    if (useRSchema()) {
+        mAStatsEventApi.writeInt32(mEventR, value);
+    } else if (useQSchema()) {
         mEventQ << value;
     }
 }
 
 void StatsEventCompat::writeInt64(int64_t value) {
-    if (mStatsEventApi) {
-        //        mStatsEventApi->writeInt64(mEventR, value);
-    } else if (!mPlatformAtLeastR) {
+    if (useRSchema()) {
+        mAStatsEventApi.writeInt64(mEventR, value);
+    } else if (useQSchema()) {
         mEventQ << value;
     }
 }
 
 void StatsEventCompat::writeFloat(float value) {
-    if (mStatsEventApi) {
-        //        mStatsEventApi->writeFloat(mEventR, value);
-    } else if (!mPlatformAtLeastR) {
+    if (useRSchema()) {
+        mAStatsEventApi.writeFloat(mEventR, value);
+    } else if (useQSchema()) {
         mEventQ << value;
     }
 }
 
 void StatsEventCompat::writeBool(bool value) {
-    if (mStatsEventApi) {
-        //        mStatsEventApi->writeBool(mEventR, value);
-    } else if (!mPlatformAtLeastR) {
+    if (useRSchema()) {
+        mAStatsEventApi.writeBool(mEventR, value);
+    } else if (useQSchema()) {
         mEventQ << value;
     }
 }
 
 void StatsEventCompat::writeByteArray(const char* buffer, size_t length) {
-    if (mStatsEventApi) {
-        //        mStatsEventApi->writeByteArray(mEventR, (const uint8_t*)buffer, length);
-    } else if (!mPlatformAtLeastR) {
+    if (useRSchema()) {
+        mAStatsEventApi.writeByteArray(mEventR, reinterpret_cast<const uint8_t*>(buffer), length);
+    } else if (useQSchema()) {
         mEventQ.AppendCharArray(buffer, length);
     }
 }
@@ -120,19 +161,19 @@
 void StatsEventCompat::writeString(const char* value) {
     if (value == nullptr) value = "";
 
-    if (mStatsEventApi) {
-        //        mStatsEventApi->writeString(mEventR, value);
-    } else if (!mPlatformAtLeastR) {
+    if (useRSchema()) {
+        mAStatsEventApi.writeString(mEventR, value);
+    } else if (useQSchema()) {
         mEventQ << value;
     }
 }
 
 void StatsEventCompat::writeAttributionChain(const int32_t* uids, size_t numUids,
                                              const vector<const char*>& tags) {
-    if (mStatsEventApi) {
-        //        mStatsEventApi->writeAttributionChain(mEventR, (const uint32_t*)uids, tags.data(),
-        //                                                (uint8_t)numUids);
-    } else if (!mPlatformAtLeastR) {
+    if (useRSchema()) {
+        mAStatsEventApi.writeAttributionChain(mEventR, (const uint32_t*)uids, tags.data(),
+                                              (uint8_t)numUids);
+    } else if (useQSchema()) {
         mEventQ.begin();
         for (size_t i = 0; i < numUids; i++) {
             mEventQ.begin();
@@ -149,8 +190,8 @@
                                           const map<int, int64_t>& int64Map,
                                           const map<int, const char*>& stringMap,
                                           const map<int, float>& floatMap) {
-    // Key value pairs are not supported with AStatsEvent.
-    if (!mPlatformAtLeastR) {
+    // AStatsEvent does not support key value pairs.
+    if (useQSchema()) {
         mEventQ.begin();
         writeKeyValuePairMap(int32Map);
         writeKeyValuePairMap(int64Map);
@@ -177,34 +218,36 @@
 template void StatsEventCompat::writeKeyValuePairMap<const char*>(const map<int, const char*>&);
 
 void StatsEventCompat::addBoolAnnotation(uint8_t annotationId, bool value) {
-    // Workaround for unused params.
-    (void)annotationId;
-    (void)value;
-    //    if (mStatsEventApi) mStatsEventApi->addBoolAnnotation(mEventR, annotationId, value);
+    if (useRSchema()) {
+        mAStatsEventApi.addBoolAnnotation(mEventR, annotationId, value);
+    }
     // Don't do anything if on Q.
 }
 
 void StatsEventCompat::addInt32Annotation(uint8_t annotationId, int32_t value) {
-    // Workaround for unused params.
-    (void)annotationId;
-    (void)value;
-    //    if (mStatsEventApi) mStatsEventApi->addInt32Annotation(mEventR, annotationId, value);
+    if (useRSchema()) {
+        mAStatsEventApi.addInt32Annotation(mEventR, annotationId, value);
+    }
     // Don't do anything if on Q.
 }
 
 int StatsEventCompat::writeToSocket() {
-    if (mStatsEventApi) {
-        //        mStatsEventApi->build(mEventR);
-        //        return mStatsEventApi->write(mEventR);
+    if (useRSchema()) {
+        mAStatsEventApi.build(mEventR);
+        return mAStatsEventApi.write(mEventR);
     }
 
-    if (!mPlatformAtLeastR) return mEventQ.write(LOG_ID_STATS);
+    if (useQSchema()) return mEventQ.write(LOG_ID_STATS);
 
-    // We reach here only if we're on R, but libstatspush_compat was unable to
+    // We reach here only if we're on R, but libstatssocket was unable to
     // be loaded using dlopen.
     return -ENOLINK;
 }
 
-bool StatsEventCompat::usesNewSchema() {
-    return mStatsEventApi != nullptr;
+bool StatsEventCompat::useRSchema() {
+    return mPlatformAtLeastR && mAStatsEventApi.initialized;
+}
+
+bool StatsEventCompat::useQSchema() {
+    return !mPlatformAtLeastR;
 }
diff --git a/libstats/push_compat/include/StatsEventCompat.h b/libstats/push_compat/include/StatsEventCompat.h
index ad423a1..00bf48b 100644
--- a/libstats/push_compat/include/StatsEventCompat.h
+++ b/libstats/push_compat/include/StatsEventCompat.h
@@ -26,6 +26,26 @@
 using std::map;
 using std::vector;
 
+struct AStatsEventApi {
+    // Indicates whether the below function pointers have been set using dlsym.
+    bool initialized = false;
+
+    AStatsEvent* (*obtain)(void);
+    void (*build)(AStatsEvent*);
+    int (*write)(AStatsEvent*);
+    void (*release)(AStatsEvent*);
+    void (*setAtomId)(AStatsEvent*, uint32_t);
+    void (*writeInt32)(AStatsEvent*, int32_t);
+    void (*writeInt64)(AStatsEvent*, int64_t);
+    void (*writeFloat)(AStatsEvent*, float);
+    void (*writeBool)(AStatsEvent*, bool);
+    void (*writeByteArray)(AStatsEvent*, const uint8_t*, size_t);
+    void (*writeString)(AStatsEvent*, const char*);
+    void (*writeAttributionChain)(AStatsEvent*, const uint32_t*, const char* const*, uint8_t);
+    void (*addBoolAnnotation)(AStatsEvent*, uint8_t, bool);
+    void (*addInt32Annotation)(AStatsEvent*, uint8_t, int32_t);
+};
+
 class StatsEventCompat {
   public:
     StatsEventCompat();
@@ -57,8 +77,7 @@
     const static bool mPlatformAtLeastR;
     static bool mAttemptedLoad;
     static std::mutex mLoadLock;
-    //    static struct AStatsEvent_apiTable* mStatsEventApi;
-    static void* mStatsEventApi;
+    static AStatsEventApi mAStatsEventApi;
 
     // non-static member variables
     AStatsEvent* mEventR = nullptr;
@@ -67,6 +86,9 @@
     template <class T>
     void writeKeyValuePairMap(const map<int, T>& keyValuePairMap);
 
-    bool usesNewSchema();
+    void initializeApiTableLocked(void* handle);
+    bool useRSchema();
+    bool useQSchema();
+
     FRIEND_TEST(StatsEventCompatTest, TestDynamicLoading);
 };
diff --git a/libstats/push_compat/tests/StatsEventCompat_test.cpp b/libstats/push_compat/tests/StatsEventCompat_test.cpp
index 2be24ec..dcb3797 100644
--- a/libstats/push_compat/tests/StatsEventCompat_test.cpp
+++ b/libstats/push_compat/tests/StatsEventCompat_test.cpp
@@ -29,10 +29,10 @@
  *
  * TODO(b/146019024): migrate to android_get_device_api_level()
  */
-const static bool mPlatformAtLeastR = GetProperty("ro.build.version.release", "") == "R" ||
+const static bool mPlatformAtLeastR = GetProperty("ro.build.version.codename", "") == "R" ||
                                       android_get_device_api_level() > __ANDROID_API_Q__;
 
 TEST(StatsEventCompatTest, TestDynamicLoading) {
     StatsEventCompat event;
-    EXPECT_EQ(mPlatformAtLeastR, event.usesNewSchema());
+    EXPECT_EQ(mPlatformAtLeastR, event.useRSchema());
 }
diff --git a/libstats/socket/include/stats_event.h b/libstats/socket/include/stats_event.h
index ff84283..3576298 100644
--- a/libstats/socket/include/stats_event.h
+++ b/libstats/socket/include/stats_event.h
@@ -29,8 +29,9 @@
  *      AStatsEvent* event = AStatsEvent_obtain();
  *
  *      AStatsEvent_setAtomId(event, atomId);
+ *      AStatsEvent_addBoolAnnotation(event, 5, false); // atom-level annotation
  *      AStatsEvent_writeInt32(event, 24);
- *      AStatsEvent_addBoolAnnotation(event, 1, true); // annotations apply to the previous field
+ *      AStatsEvent_addBoolAnnotation(event, 1, true); // annotation for preceding atom field
  *      AStatsEvent_addInt32Annotation(event, 2, 128);
  *      AStatsEvent_writeFloat(event, 2.0);
  *
@@ -38,13 +39,8 @@
  *      AStatsEvent_write(event);
  *      AStatsEvent_release(event);
  *
- * Notes:
- *    (a) write_<type>() and add_<type>_annotation() should be called in the order that fields
- *        and annotations are defined in the atom.
- *    (b) set_atom_id() can be called anytime before stats_event_write().
- *    (c) add_<type>_annotation() calls apply to the previous field.
- *    (d) If errors occur, stats_event_write() will write a bitmask of the errors to the socket.
- *    (e) All strings should be encoded using UTF8.
+ * Note that calls to add atom fields and annotations should be made in the
+ * order that they are defined in the atom.
  */
 
 #ifdef __cplusplus
@@ -84,7 +80,7 @@
 int AStatsEvent_write(AStatsEvent* event);
 
 /**
- * Frees the memory held by this StatsEvent
+ * Frees the memory held by this StatsEvent.
  *
  * After calling this, the StatsEvent must not be used or modified in any way.
  */
@@ -92,6 +88,8 @@
 
 /**
  * Sets the atom id for this StatsEvent.
+ *
+ * This function should be called immediately after AStatsEvent_obtain.
  **/
 void AStatsEvent_setAtomId(AStatsEvent* event, uint32_t atomId);
 
diff --git a/libstats/socket/stats_event.c b/libstats/socket/stats_event.c
index b045d93..24d2ea8 100644
--- a/libstats/socket/stats_event.c
+++ b/libstats/socket/stats_event.c
@@ -29,7 +29,6 @@
 #define POS_NUM_ELEMENTS 1
 #define POS_TIMESTAMP (POS_NUM_ELEMENTS + sizeof(uint8_t))
 #define POS_ATOM_ID (POS_TIMESTAMP + sizeof(uint8_t) + sizeof(uint64_t))
-#define POS_FIRST_FIELD (POS_ATOM_ID + sizeof(uint8_t) + sizeof(uint32_t))
 
 /* LIMITS */
 #define MAX_ANNOTATION_COUNT 15
@@ -66,8 +65,11 @@
 // within a buf. Also includes other required fields.
 struct AStatsEvent {
     uint8_t* buf;
-    size_t lastFieldPos;  // location of last field within the buf
-    size_t size;          // number of valid bytes within buffer
+    // Location of last field within the buf. Here, field denotes either a
+    // metadata field (e.g. timestamp) or an atom field.
+    size_t lastFieldPos;
+    // Number of valid bytes within the buffer.
+    size_t size;
     uint32_t numElements;
     uint32_t atomId;
     uint32_t errors;
@@ -85,20 +87,21 @@
 AStatsEvent* AStatsEvent_obtain() {
     AStatsEvent* event = malloc(sizeof(AStatsEvent));
     event->buf = (uint8_t*)calloc(MAX_EVENT_PAYLOAD, 1);
-    event->buf[0] = OBJECT_TYPE;
+    event->lastFieldPos = 0;
+    event->size = 2;  // reserve first two bytes for outer event type and number of elements
+    event->numElements = 0;
     event->atomId = 0;
     event->errors = 0;
     event->truncate = true;  // truncate for both pulled and pushed atoms
     event->built = false;
 
-    // place the timestamp
-    uint64_t timestampNs = get_elapsed_realtime_ns();
-    event->buf[POS_TIMESTAMP] = INT64_TYPE;
-    memcpy(&event->buf[POS_TIMESTAMP + sizeof(uint8_t)], &timestampNs, sizeof(timestampNs));
+    event->buf[0] = OBJECT_TYPE;
+    AStatsEvent_writeInt64(event, get_elapsed_realtime_ns());  // write the timestamp
 
-    event->numElements = 1;
-    event->lastFieldPos = 0;  // 0 since we haven't written a field yet
-    event->size = POS_FIRST_FIELD;
+    // Force client to set atom id immediately (this is required for atom-level
+    // annotations to be written correctly). All atom field and annotation
+    // writes will fail until the atom id is set because event->errors != 0.
+    event->errors |= ERROR_NO_ATOM_ID;
 
     return event;
 }
@@ -109,10 +112,12 @@
 }
 
 void AStatsEvent_setAtomId(AStatsEvent* event, uint32_t atomId) {
+    if ((event->errors & ERROR_NO_ATOM_ID) == 0) return;
+
+    // Clear the ERROR_NO_ATOM_ID bit.
+    event->errors &= ~ERROR_NO_ATOM_ID;
     event->atomId = atomId;
-    event->buf[POS_ATOM_ID] = INT32_TYPE;
-    memcpy(&event->buf[POS_ATOM_ID + sizeof(uint8_t)], &atomId, sizeof(atomId));
-    event->numElements++;
+    AStatsEvent_writeInt32(event, atomId);
 }
 
 // Overwrites the timestamp populated in AStatsEvent_obtain with a custom
@@ -306,23 +311,23 @@
 void AStatsEvent_build(AStatsEvent* event) {
     if (event->built) return;
 
-    if (event->atomId == 0) event->errors |= ERROR_NO_ATOM_ID;
-
-    if (event->numElements > MAX_BYTE_VALUE) {
-        event->errors |= ERROR_TOO_MANY_FIELDS;
-    } else {
-        event->buf[POS_NUM_ELEMENTS] = event->numElements;
-    }
+    if (event->numElements > MAX_BYTE_VALUE) event->errors |= ERROR_TOO_MANY_FIELDS;
 
     // If there are errors, rewrite buffer.
     if (event->errors) {
-        event->buf[POS_NUM_ELEMENTS] = 3;
-        event->buf[POS_FIRST_FIELD] = ERROR_TYPE;
-        memcpy(&event->buf[POS_FIRST_FIELD + sizeof(uint8_t)], &event->errors,
-               sizeof(event->errors));
-        event->size = POS_FIRST_FIELD + sizeof(uint8_t) + sizeof(uint32_t);
+        // Discard everything after the atom id (including atom-level
+        // annotations). This leaves only two elements (timestamp and atom id).
+        event->numElements = 2;
+        // Reset number of atom-level annotations to 0.
+        event->buf[POS_ATOM_ID] = INT32_TYPE;
+        // Now, write errors to the buffer immediately after the atom id.
+        event->size = POS_ATOM_ID + sizeof(uint8_t) + sizeof(uint32_t);
+        start_field(event, ERROR_TYPE);
+        append_int32(event, event->errors);
     }
 
+    event->buf[POS_NUM_ELEMENTS] = event->numElements;
+
     // Truncate the buffer to the appropriate length in order to limit our
     // memory usage.
     if (event->truncate) event->buf = (uint8_t*)realloc(event->buf, event->size);
diff --git a/libstats/socket/tests/stats_event_test.cpp b/libstats/socket/tests/stats_event_test.cpp
index 69d0a9b..04eff36 100644
--- a/libstats/socket/tests/stats_event_test.cpp
+++ b/libstats/socket/tests/stats_event_test.cpp
@@ -89,7 +89,7 @@
 }
 
 void checkMetadata(uint8_t** buffer, uint8_t numElements, int64_t startTime, int64_t endTime,
-                   uint32_t atomId) {
+                   uint32_t atomId, uint8_t numAtomLevelAnnotations = 0) {
     // All events start with OBJECT_TYPE id.
     checkTypeHeader(buffer, OBJECT_TYPE);
 
@@ -104,7 +104,7 @@
     EXPECT_LE(timestamp, endTime);
 
     // Check atom id
-    checkTypeHeader(buffer, INT32_TYPE);
+    checkTypeHeader(buffer, INT32_TYPE, numAtomLevelAnnotations);
     checkScalar(buffer, atomId);
 }
 
@@ -240,7 +240,7 @@
     AStatsEvent_release(event);
 }
 
-TEST(StatsEventTest, TestAnnotations) {
+TEST(StatsEventTest, TestFieldAnnotations) {
     uint32_t atomId = 100;
 
     // first element information
@@ -259,7 +259,7 @@
 
     int64_t startTime = android::elapsedRealtimeNano();
     AStatsEvent* event = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(event, 100);
+    AStatsEvent_setAtomId(event, atomId);
     AStatsEvent_writeBool(event, boolValue);
     AStatsEvent_addBoolAnnotation(event, boolAnnotation1Id, boolAnnotation1Value);
     AStatsEvent_addInt32Annotation(event, boolAnnotation2Id, boolAnnotation2Value);
@@ -292,6 +292,45 @@
     AStatsEvent_release(event);
 }
 
+TEST(StatsEventTest, TestAtomLevelAnnotations) {
+    uint32_t atomId = 100;
+    // atom-level annotation information
+    uint8_t boolAnnotationId = 1;
+    uint8_t int32AnnotationId = 2;
+    bool boolAnnotationValue = false;
+    int32_t int32AnnotationValue = 5;
+
+    float fieldValue = -3.5;
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    AStatsEvent* event = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(event, atomId);
+    AStatsEvent_addBoolAnnotation(event, boolAnnotationId, boolAnnotationValue);
+    AStatsEvent_addInt32Annotation(event, int32AnnotationId, int32AnnotationValue);
+    AStatsEvent_writeFloat(event, fieldValue);
+    AStatsEvent_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId,
+                  /*numAtomLevelAnnotations=*/2);
+
+    // check atom-level annotations
+    checkAnnotation(&buffer, boolAnnotationId, BOOL_TYPE, boolAnnotationValue);
+    checkAnnotation(&buffer, int32AnnotationId, INT32_TYPE, int32AnnotationValue);
+
+    // check first element
+    checkTypeHeader(&buffer, FLOAT_TYPE);
+    checkScalar(&buffer, fieldValue);
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(AStatsEvent_getErrors(event), 0);
+    AStatsEvent_release(event);
+}
+
 TEST(StatsEventTest, TestNoAtomIdError) {
     AStatsEvent* event = AStatsEvent_obtain();
     // Don't set the atom id in order to trigger the error.
diff --git a/rootdir/init.rc b/rootdir/init.rc
index c59f911..201fb12 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -16,6 +16,11 @@
     # Disable sysrq from keyboard
     write /proc/sys/kernel/sysrq 0
 
+    # Android doesn't need kernel module autoloading, and it causes SELinux
+    # denials.  So disable it by setting modprobe to the empty string.  Note: to
+    # explicitly set a sysctl to an empty string, a trailing newline is needed.
+    write /proc/sys/kernel/modprobe \n
+
     # Set the security context of /adb_keys if present.
     restorecon /adb_keys