Merge "init: use ErrnoErrorf() instead of ErrnoError() << StringPrintf()"
diff --git a/adb/apex/Android.bp b/adb/apex/Android.bp
index 75c4ed9..a6b1e78 100644
--- a/adb/apex/Android.bp
+++ b/adb/apex/Android.bp
@@ -21,7 +21,7 @@
 }
 
 // adbd apex with INT_MAX version code, to allow for upgrade/rollback testing.
-apex {
+apex_test {
     name: "test_com.android.adbd",
     defaults: ["com.android.adbd-defaults"],
     manifest: "test_apex_manifest.json",
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 73dcde1..2bcd0a6 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -227,16 +227,20 @@
         return 1;
     }
 
-    copy_to_file(local_fd.get(), remote_fd.get());
+    if (!copy_to_file(local_fd.get(), remote_fd.get())) {
+        fprintf(stderr, "adb: failed to install: copy_to_file: %s: %s", file, strerror(errno));
+        return 1;
+    }
 
     char buf[BUFSIZ];
     read_status_line(remote_fd.get(), buf, sizeof(buf));
-    if (!strncmp("Success", buf, 7)) {
-        fputs(buf, stdout);
-        return 0;
+    if (strncmp("Success", buf, 7) != 0) {
+        fprintf(stderr, "adb: failed to install %s: %s", file, buf);
+        return 1;
     }
-    fprintf(stderr, "adb: failed to install %s: %s", file, buf);
-    return 1;
+
+    fputs(buf, stdout);
+    return 0;
 }
 
 static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy) {
@@ -455,7 +459,12 @@
             goto finalize_session;
         }
 
-        copy_to_file(local_fd.get(), remote_fd.get());
+        if (!copy_to_file(local_fd.get(), remote_fd.get())) {
+            fprintf(stderr, "adb: failed to write \"%s\": %s\n", file, strerror(errno));
+            success = false;
+            goto finalize_session;
+        }
+
         read_status_line(remote_fd.get(), buf, sizeof(buf));
 
         if (strncmp("Success", buf, 7)) {
@@ -634,7 +643,11 @@
                 goto finalize_multi_package_session;
             }
 
-            copy_to_file(local_fd.get(), remote_fd.get());
+            if (!copy_to_file(local_fd.get(), remote_fd.get())) {
+                fprintf(stderr, "adb: failed to write %s: %s\n", split.c_str(), strerror(errno));
+                goto finalize_multi_package_session;
+            }
+
             read_status_line(remote_fd.get(), buf, sizeof(buf));
 
             if (strncmp("Success", buf, 7)) {
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index a6d7e31..c302965 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -352,7 +352,8 @@
 #endif
 }
 
-void copy_to_file(int inFd, int outFd) {
+bool copy_to_file(int inFd, int outFd) {
+    bool result = true;
     std::vector<char> buf(64 * 1024);
     int len;
     long total = 0;
@@ -375,6 +376,7 @@
         }
         if (len < 0) {
             D("copy_to_file(): read failed: %s", strerror(errno));
+            result = false;
             break;
         }
         if (outFd == STDOUT_FILENO) {
@@ -388,7 +390,8 @@
 
     stdinout_raw_epilogue(inFd, outFd, old_stdin_mode, old_stdout_mode);
 
-    D("copy_to_file() finished after %lu bytes", total);
+    D("copy_to_file() finished with %s after %lu bytes", result ? "success" : "failure", total);
+    return result;
 }
 
 static void send_window_size_change(int fd, std::unique_ptr<ShellProtocol>& shell) {
diff --git a/adb/client/commandline.h b/adb/client/commandline.h
index ab77b29..b9dee56 100644
--- a/adb/client/commandline.h
+++ b/adb/client/commandline.h
@@ -100,7 +100,7 @@
 
 int adb_commandline(int argc, const char** argv);
 
-void copy_to_file(int inFd, int outFd);
+bool copy_to_file(int inFd, int outFd);
 
 // Connects to the device "shell" service with |command| and prints the
 // resulting output.
diff --git a/base/expected_test.cpp b/base/expected_test.cpp
index a74bc1d..6c3d421 100644
--- a/base/expected_test.cpp
+++ b/base/expected_test.cpp
@@ -499,24 +499,6 @@
   EXPECT_TRUE(e4 != e3);
 }
 
-TEST(Expected, testCompareWithSameValue) {
-  exp_int e = 10;
-  int value = 10;
-  EXPECT_TRUE(e == value);
-  EXPECT_TRUE(value == e);
-  EXPECT_FALSE(e != value);
-  EXPECT_FALSE(value != e);
-}
-
-TEST(Expected, testCompareWithDifferentValue) {
-  exp_int e = 10;
-  int value = 20;
-  EXPECT_FALSE(e == value);
-  EXPECT_FALSE(value == e);
-  EXPECT_TRUE(e != value);
-  EXPECT_TRUE(value != e);
-}
-
 TEST(Expected, testCompareWithSameError) {
   exp_int e = unexpected(10);
   exp_int::unexpected_type error = 10;
@@ -594,7 +576,7 @@
   EXPECT_EQ(-1, divide(10, 0).error().cause);
 
   EXPECT_TRUE(divide(10, 3));
-  EXPECT_EQ(QR(3, 1), divide(10, 3));
+  EXPECT_EQ(QR(3, 1), *divide(10, 3));
 }
 
 TEST(Expected, testPair) {
diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h
index b3f5adb..9603bb1 100644
--- a/base/include/android-base/expected.h
+++ b/base/include/android-base/expected.h
@@ -331,6 +331,7 @@
 
   constexpr explicit operator bool() const noexcept { return has_value(); }
   constexpr bool has_value() const noexcept { return var_.index() == 0; }
+  constexpr bool ok() const noexcept { return has_value(); }
 
   constexpr const T& value() const& { return std::get<T>(var_); }
   constexpr T& value() & { return std::get<T>(var_); }
@@ -366,16 +367,6 @@
   template<class T1, class E1, class T2, class E2>
   friend constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y);
 
-  // comparison with T
-  template<class T1, class E1, class T2>
-  friend constexpr bool operator==(const expected<T1, E1>&, const T2&);
-  template<class T1, class E1, class T2>
-  friend constexpr bool operator==(const T2&, const expected<T1, E1>&);
-  template<class T1, class E1, class T2>
-  friend constexpr bool operator!=(const expected<T1, E1>&, const T2&);
-  template<class T1, class E1, class T2>
-  friend constexpr bool operator!=(const T2&, const expected<T1, E1>&);
-
   // Comparison with unexpected<E>
   template<class T1, class E1, class E2>
   friend constexpr bool operator==(const expected<T1, E1>&, const unexpected<E2>&);
@@ -410,24 +401,6 @@
   return !(x == y);
 }
 
-// comparison with T
-template<class T1, class E1, class T2>
-constexpr bool operator==(const expected<T1, E1>& x, const T2& y) {
-  return x.has_value() && (*x == y);
-}
-template<class T1, class E1, class T2>
-constexpr bool operator==(const T2& x, const expected<T1, E1>& y) {
-  return y.has_value() && (x == *y);
-}
-template<class T1, class E1, class T2>
-constexpr bool operator!=(const expected<T1, E1>& x, const T2& y) {
-  return !x.has_value() || (*x != y);
-}
-template<class T1, class E1, class T2>
-constexpr bool operator!=(const T2& x, const expected<T1, E1>& y) {
-  return !y.has_value() || (x != *y);
-}
-
 // Comparison with unexpected<E>
 template<class T1, class E1, class E2>
 constexpr bool operator==(const expected<T1, E1>& x, const unexpected<E2>& y) {
@@ -585,6 +558,7 @@
   // observers
   constexpr explicit operator bool() const noexcept { return has_value(); }
   constexpr bool has_value() const noexcept { return var_.index() == 0; }
+  constexpr bool ok() const noexcept { return has_value(); }
 
   constexpr void value() const& { if (!has_value()) std::get<0>(var_); }
 
diff --git a/base/include/android-base/result.h b/base/include/android-base/result.h
index e5d90c8..4a59552 100644
--- a/base/include/android-base/result.h
+++ b/base/include/android-base/result.h
@@ -212,5 +212,28 @@
 template <typename T>
 using Result = android::base::expected<T, ResultError>;
 
+// Macros for testing the results of functions that return android::base::Result.
+// These also work with base::android::expected.
+
+#define CHECK_RESULT_OK(stmt)       \
+  do {                              \
+    const auto& tmp = (stmt);       \
+    CHECK(tmp.ok()) << tmp.error(); \
+  } while (0)
+
+#define ASSERT_RESULT_OK(stmt)            \
+  do {                                    \
+    const auto& tmp = (stmt);             \
+    ASSERT_TRUE(tmp.ok()) << tmp.error(); \
+  } while (0)
+
+#define EXPECT_RESULT_OK(stmt)            \
+  do {                                    \
+    auto tmp = (stmt);                    \
+    EXPECT_TRUE(tmp.ok()) << tmp.error(); \
+  } while (0)
+
+// TODO: Maybe add RETURN_IF_ERROR() and ASSIGN_OR_RETURN()
+
 }  // namespace base
 }  // namespace android
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 6b8a09a..9ffe5dd 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -32,6 +32,8 @@
 #include <memory>
 #include <regex>
 #include <string>
+#include <string_view>
+#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -50,16 +52,133 @@
 
 namespace {
 
+struct AtomInfo {
+  int32_t atom;
+  int32_t event;
+};
+
+// Maps BootEvent used inside bootstat into statsd atom defined in
+// frameworks/base/cmds/statsd/src/atoms.proto.
+const std::unordered_map<std::string_view, AtomInfo> kBootEventToAtomInfo = {
+    // ELAPSED_TIME
+    {"ro.boottime.init",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__ANDROID_INIT_STAGE_1}},
+    {"boot_complete",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__BOOT_COMPLETE}},
+    {"boot_decryption_complete",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__BOOT_COMPLETE_ENCRYPTION}},
+    {"boot_complete_no_encryption",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__BOOT_COMPLETE_NO_ENCRYPTION}},
+    {"boot_complete_post_decrypt",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__BOOT_COMPLETE_POST_DECRYPT}},
+    {"factory_reset_boot_complete",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FACTORY_RESET_BOOT_COMPLETE}},
+    {"factory_reset_boot_complete_no_encryption",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::
+          BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FACTORY_RESET_BOOT_COMPLETE_NO_ENCRYPTION}},
+    {"factory_reset_boot_complete_post_decrypt",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::
+          BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FACTORY_RESET_BOOT_COMPLETE_POST_DECRYPT}},
+    {"ota_boot_complete",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__OTA_BOOT_COMPLETE}},
+    {"ota_boot_complete_no_encryption",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__OTA_BOOT_COMPLETE_NO_ENCRYPTION}},
+    {"ota_boot_complete_post_decrypt",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__OTA_BOOT_COMPLETE_POST_DECRYPT}},
+    {"post_decrypt_time_elapsed",
+     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__POST_DECRYPT}},
+    // DURATION
+    {"absolute_boot_time",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__ABSOLUTE_BOOT_TIME}},
+    {"boottime.bootloader.1BLE",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_FIRST_STAGE_EXEC}},
+    {"boottime.bootloader.1BLL",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_FIRST_STAGE_LOAD}},
+    {"boottime.bootloader.KL",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_KERNEL_LOAD}},
+    {"boottime.bootloader.2BLE",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_SECOND_STAGE_EXEC}},
+    {"boottime.bootloader.2BLL",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_SECOND_STAGE_LOAD}},
+    {"boottime.bootloader.SW",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_UI_WAIT}},
+    {"boottime.bootloader.total",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_TOTAL}},
+    {"boottime.init.cold_boot_wait",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__COLDBOOT_WAIT}},
+    {"time_since_factory_reset",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__FACTORY_RESET_TIME_SINCE_RESET}},
+    {"ro.boottime.init.first_stage",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__ANDROID_INIT_STAGE_1}},
+    {"ro.boottime.init.selinux",
+     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
+      android::util::BOOT_TIME_EVENT_DURATION__EVENT__SELINUX_INIT}},
+    // UTC_TIME
+    {"factory_reset",
+     {android::util::BOOT_TIME_EVENT_UTC_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_UTC_TIME__EVENT__FACTORY_RESET_RESET_TIME}},
+    {"factory_reset_current_time",
+     {android::util::BOOT_TIME_EVENT_UTC_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_UTC_TIME__EVENT__FACTORY_RESET_CURRENT_TIME}},
+    {"factory_reset_record_value",
+     {android::util::BOOT_TIME_EVENT_UTC_TIME_REPORTED,
+      android::util::BOOT_TIME_EVENT_UTC_TIME__EVENT__FACTORY_RESET_RECORD_VALUE}},
+    // ERROR_CODE
+    {"factory_reset_current_time_failure",
+     {android::util::BOOT_TIME_EVENT_ERROR_CODE_REPORTED,
+      android::util::BOOT_TIME_EVENT_ERROR_CODE__EVENT__FACTORY_RESET_CURRENT_TIME_FAILURE}},
+};
+
 // Scans the boot event record store for record files and logs each boot event
 // via EventLog.
 void LogBootEvents() {
   BootEventRecordStore boot_event_store;
-
   auto events = boot_event_store.GetAllBootEvents();
-  // TODO(b/148575354): Replace with statsd.
-  // for (auto i = events.cbegin(); i != events.cend(); ++i) {
-  //   android::metricslogger::LogHistogram(i->first, i->second);
-  // }
+  std::vector<std::string_view> notSupportedEvents;
+  for (const auto& event : events) {
+    const auto& name = event.first;
+    const auto& info = kBootEventToAtomInfo.find(name);
+    if (info != kBootEventToAtomInfo.end()) {
+      if (info->second.atom == android::util::BOOT_TIME_EVENT_ERROR_CODE_REPORTED) {
+        android::util::stats_write(static_cast<int32_t>(info->second.atom),
+                                   static_cast<int32_t>(info->second.event),
+                                   static_cast<int32_t>(event.second));
+      } else {
+        android::util::stats_write(static_cast<int32_t>(info->second.atom),
+                                   static_cast<int32_t>(info->second.event),
+                                   static_cast<int64_t>(event.second));
+      }
+    } else {
+      notSupportedEvents.push_back(name);
+    }
+  }
+  if (!notSupportedEvents.empty()) {
+    LOG(WARNING) << "LogBootEvents, atomInfo not defined for events:"
+                 << android::base::Join(notSupportedEvents, ',');
+  }
 }
 
 // Records the named boot |event| to the record store. If |value| is non-empty
@@ -1250,10 +1369,13 @@
   time_t current_time_utc = time(nullptr);
 
   if (current_time_utc < 0) {
-    // TODO(b/148575354): Replace with statsd.
     // UMA does not display negative values in buckets, so convert to positive.
-    // android::metricslogger::LogHistogram("factory_reset_current_time_failure",
-    //                                      std::abs(current_time_utc));
+    // Logging via BootEventRecordStore.
+    android::util::stats_write(
+        static_cast<int32_t>(android::util::BOOT_TIME_EVENT_ERROR_CODE_REPORTED),
+        static_cast<int32_t>(
+            android::util::BOOT_TIME_EVENT_ERROR_CODE__EVENT__FACTORY_RESET_CURRENT_TIME_FAILURE),
+        static_cast<int32_t>(std::abs(current_time_utc)));
 
     // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
     // is losing records somehow.
@@ -1261,8 +1383,11 @@
                                            std::abs(current_time_utc));
     return;
   } else {
-    // TODO(b/148575354): Replace with statsd.
-    // android::metricslogger::LogHistogram("factory_reset_current_time", current_time_utc);
+    android::util::stats_write(
+        static_cast<int32_t>(android::util::BOOT_TIME_EVENT_UTC_TIME_REPORTED),
+        static_cast<int32_t>(
+            android::util::BOOT_TIME_EVENT_UTC_TIME__EVENT__FACTORY_RESET_CURRENT_TIME),
+        static_cast<int64_t>(current_time_utc));
 
     // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
     // is losing records somehow.
@@ -1282,8 +1407,11 @@
   // Calculate and record the difference in time between now and the
   // factory_reset time.
   time_t factory_reset_utc = record.second;
-  // TODO(b/148575354): Replace with statsd.
-  // android::metricslogger::LogHistogram("factory_reset_record_value", factory_reset_utc);
+  android::util::stats_write(
+      static_cast<int32_t>(android::util::BOOT_TIME_EVENT_UTC_TIME_REPORTED),
+      static_cast<int32_t>(
+          android::util::BOOT_TIME_EVENT_UTC_TIME__EVENT__FACTORY_RESET_RECORD_VALUE),
+      static_cast<int64_t>(factory_reset_utc));
 
   // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
   // is losing records somehow.
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 7a88aa3..f5daf91 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -96,6 +96,10 @@
     export_header_lib_headers: [
         "libfiemap_headers",
     ],
+    required: [
+        "e2freefrag",
+        "e2fsdroid",
+    ],
 }
 
 // Two variants of libfs_mgr are provided: libfs_mgr and libfs_mgr_binder.
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index a947b4e..705d4e3 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -2,6 +2,18 @@
   "presubmit": [
     {
       "name": "libdm_test"
+    },
+    {
+      "name": "liblp_test"
+    },
+    {
+      "name": "fiemap_image_test_presubmit"
+    },
+    {
+      "name": "fiemap_writer_test"
+    },
+    {
+      "name": "vts_libsnapshot_test_presubmit"
     }
   ]
 }
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index 1bf457f..2fd463c 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -104,4 +104,34 @@
     srcs: [
         "image_test.cpp",
     ],
+    test_suites: ["device-tests"],
+    auto_gen_config: true,
+    require_root: true,
+}
+
+/* BUG(148874852) temporary test */
+cc_test {
+    name: "fiemap_image_test_presubmit",
+    cppflags: [
+        "-DSKIP_TEST_IN_PRESUBMIT",
+    ],
+    static_libs: [
+        "libdm",
+        "libext4_utils",
+        "libfs_mgr",
+        "liblp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcrypto",
+        "libcrypto_utils",
+        "libcutils",
+        "liblog",
+    ],
+    srcs: [
+        "image_test.cpp",
+    ],
+    test_suites: ["device-tests"],
+    auto_gen_config: true,
+    require_root: true,
 }
diff --git a/fs_mgr/libfiemap/fiemap_writer.cpp b/fs_mgr/libfiemap/fiemap_writer.cpp
index b911234..4dd4bcc 100644
--- a/fs_mgr/libfiemap/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer.cpp
@@ -526,11 +526,7 @@
 }
 
 static bool IsLastExtent(const fiemap_extent* extent) {
-    if (!(extent->fe_flags & FIEMAP_EXTENT_LAST)) {
-        LOG(ERROR) << "Extents are being received out-of-order";
-        return false;
-    }
-    return true;
+    return !!(extent->fe_flags & FIEMAP_EXTENT_LAST);
 }
 
 static bool FiemapToExtents(struct fiemap* fiemap, std::vector<struct fiemap_extent>* extents,
@@ -552,7 +548,10 @@
         fiemap_extent* next = &fiemap->fm_extents[i];
 
         // Make sure extents are returned in order
-        if (next != last_extent && IsLastExtent(next)) return false;
+        if (next != last_extent && IsLastExtent(next)) {
+            LOG(ERROR) << "Extents are being received out-of-order";
+            return false;
+        }
 
         // Check if extent's flags are valid
         if (!IsValidExtent(next, file_path)) return false;
@@ -592,8 +591,7 @@
         return false;
     }
 
-    uint64_t fiemap_size =
-            sizeof(struct fiemap_extent) + num_extents * sizeof(struct fiemap_extent);
+    uint64_t fiemap_size = sizeof(struct fiemap) + num_extents * sizeof(struct fiemap_extent);
     auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, fiemap_size), free);
     if (buffer == nullptr) {
         LOG(ERROR) << "Failed to allocate memory for fiemap";
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 80c340f..5388b44 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -212,6 +212,9 @@
 }
 
 TEST_F(ImageTest, IndirectMount) {
+#ifdef SKIP_TEST_IN_PRESUBMIT
+    GTEST_SKIP() << "WIP failure b/148874852";
+#endif
     // Create a simple wrapper around the base device that we'll mount from
     // instead. This will simulate the code paths for dm-crypt/default-key/bow
     // and force us to use device-mapper rather than loop devices.
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index 8770a6b..50de42c 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -266,8 +266,10 @@
     return avb_handle;
 }
 
-AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry) {
-    if (fstab_entry.avb_keys.empty()) {
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,
+                                            const std::vector<std::string>& preload_avb_key_blobs) {
+    // At least one of the following should be provided for public key matching.
+    if (preload_avb_key_blobs.empty() && fstab_entry.avb_keys.empty()) {
         LERROR << "avb_keys=/path/to/key(s) is missing for " << fstab_entry.mount_point;
         return nullptr;
     }
@@ -309,18 +311,36 @@
             return nullptr;
     }
 
-    // fstab_entry.avb_keys might be either a directory containing multiple keys,
-    // or a string indicating multiple keys separated by ':'.
-    std::vector<std::string> allowed_avb_keys;
-    auto list_avb_keys_in_dir = ListFiles(fstab_entry.avb_keys);
-    if (list_avb_keys_in_dir) {
-        std::sort(list_avb_keys_in_dir->begin(), list_avb_keys_in_dir->end());
-        allowed_avb_keys = *list_avb_keys_in_dir;
-    } else {
-        allowed_avb_keys = Split(fstab_entry.avb_keys, ":");
+    bool public_key_match = false;
+    // Performs key matching for preload_avb_key_blobs first, if it is present.
+    if (!public_key_data.empty() && !preload_avb_key_blobs.empty()) {
+        if (std::find(preload_avb_key_blobs.begin(), preload_avb_key_blobs.end(),
+                      public_key_data) != preload_avb_key_blobs.end()) {
+            public_key_match = true;
+        }
+    }
+    // Performs key matching for fstab_entry.avb_keys if necessary.
+    // Note that it is intentional to match both preload_avb_key_blobs and fstab_entry.avb_keys.
+    // Some keys might only be availble before init chroots into /system, e.g., /avb/key1
+    // in the first-stage ramdisk, while other keys might only be available after the chroot,
+    // e.g., /system/etc/avb/key2.
+    if (!public_key_data.empty() && !public_key_match) {
+        // fstab_entry.avb_keys might be either a directory containing multiple keys,
+        // or a string indicating multiple keys separated by ':'.
+        std::vector<std::string> allowed_avb_keys;
+        auto list_avb_keys_in_dir = ListFiles(fstab_entry.avb_keys);
+        if (list_avb_keys_in_dir) {
+            std::sort(list_avb_keys_in_dir->begin(), list_avb_keys_in_dir->end());
+            allowed_avb_keys = *list_avb_keys_in_dir;
+        } else {
+            allowed_avb_keys = Split(fstab_entry.avb_keys, ":");
+        }
+        if (ValidatePublicKeyBlob(public_key_data, allowed_avb_keys)) {
+            public_key_match = true;
+        }
     }
 
-    if (!ValidatePublicKeyBlob(public_key_data, allowed_avb_keys)) {
+    if (!public_key_match) {
         avb_handle->status_ = AvbHandleStatus::kVerificationError;
         LWARNING << "Found unknown public key used to sign " << fstab_entry.mount_point;
         if (!allow_verification_error) {
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
index 521f2d5..4702e68 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
@@ -85,8 +85,15 @@
     // TODO(bowgotsai): remove Open() and switch to LoadAndVerifyVbmeta().
     static AvbUniquePtr Open();                 // loads inline vbmeta, via libavb.
     static AvbUniquePtr LoadAndVerifyVbmeta();  // loads inline vbmeta.
-    static AvbUniquePtr LoadAndVerifyVbmeta(
-            const FstabEntry& fstab_entry);     // loads offline vbmeta.
+
+    // The caller can specify optional preload_avb_key_blobs for public key matching.
+    // This is mostly for init to preload AVB keys before chroot into /system.
+    // Both preload_avb_key_blobs and fstab_entry.avb_keys (file paths) will be used
+    // for public key matching.
+    static AvbUniquePtr LoadAndVerifyVbmeta(  // loads offline vbmeta.
+            const FstabEntry& fstab_entry,
+            const std::vector<std::string>& preload_avb_key_blobs = {});
+
     static AvbUniquePtr LoadAndVerifyVbmeta(    // loads offline vbmeta.
             const std::string& partition_name, const std::string& ab_suffix,
             const std::string& ab_other_suffix, const std::string& expected_public_key,
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index ad48b82..bc177a0 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -95,6 +95,16 @@
     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 {
@@ -150,8 +160,8 @@
     ],
 }
 
-cc_test {
-    name: "libsnapshot_test",
+cc_defaults {
+    name: "libsnapshot_test_defaults",
     defaults: ["libsnapshot_defaults"],
     srcs: [
         "partition_cow_creator_test.cpp",
@@ -163,26 +173,48 @@
         "android.hardware.boot@1.1",
         "libbinder",
         "libcrypto",
-        "libfs_mgr",
-        "libgsi",
         "libhidlbase",
-        "liblp",
         "libprotobuf-cpp-lite",
         "libsparse",
         "libutils",
         "libz",
+
+        // TODO(b/148818798): remove when parent bug is fixed
+        "libutilscallstack",
     ],
     static_libs: [
+        "libfs_mgr",
+        "libgsi",
         "libgmock",
+        "liblp",
         "libsnapshot",
         "libsnapshot_test_helpers",
     ],
     header_libs: [
         "libstorage_literals_headers",
     ],
+    test_suites: [
+        "vts-core",
+        "device-tests"
+    ],
+    test_min_api_level: 29,
+    auto_gen_config: true,
     require_root: true,
 }
 
+cc_test {
+    name: "vts_libsnapshot_test",
+    defaults: ["libsnapshot_test_defaults"],
+}
+
+cc_test {
+    name: "vts_libsnapshot_test_presubmit",
+    defaults: ["libsnapshot_test_defaults"],
+    cppflags: [
+        "-DSKIP_TEST_IN_PRESUBMIT",
+    ],
+}
+
 cc_binary {
     name: "snapshotctl",
     srcs: [
@@ -207,6 +239,9 @@
         "liblp",
         "libprotobuf-cpp-lite",
         "libutils",
+
+        // TODO(b/148818798): remove when parent bug is fixed.
+        "libutilscallstack",
     ],
     init_rc: [
         "snapshotctl.rc",
diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
index bacb41c..0e90100 100644
--- a/fs_mgr/libsnapshot/device_info.cpp
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -22,6 +22,7 @@
 namespace snapshot {
 
 #ifdef LIBSNAPSHOT_USE_HAL
+using android::hardware::boot::V1_0::BoolResult;
 using android::hardware::boot::V1_0::CommandResult;
 #endif
 
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index e786bc9..ed92dd7 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -174,6 +174,7 @@
     // 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();
@@ -273,6 +274,7 @@
     FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
     FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback);
     FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery);
+    FRIEND_TEST(SnapshotUpdateTest, FullUpdateFlow);
     FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow);
     FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery);
     FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
@@ -374,7 +376,7 @@
     bool HandleCancelledUpdate(LockedFile* lock);
 
     // Helper for HandleCancelledUpdate. Assumes booting from new slot.
-    bool HandleCancelledUpdateOnNewSlot(LockedFile* lock);
+    bool AreAllSnapshotsCancelled(LockedFile* lock);
 
     // Remove artifacts created by the update process, such as snapshots, and
     // set the update state to None.
@@ -439,7 +441,7 @@
     std::string GetSnapshotStatusFilePath(const std::string& name);
 
     std::string GetSnapshotBootIndicatorPath();
-    void RemoveSnapshotBootIndicator();
+    std::string GetRollbackIndicatorPath();
 
     // Return the name of the device holding the "snapshot" or "snapshot-merge"
     // target. This may not be the final device presented via MapSnapshot(), if
@@ -503,6 +505,8 @@
     friend std::ostream& operator<<(std::ostream& os, SnapshotManager::Slot slot);
     Slot GetCurrentSlot();
 
+    std::string ReadUpdateSourceSlotSuffix();
+
     std::string gsid_dir_;
     std::string metadata_dir_;
     std::unique_ptr<IDeviceInfo> device_;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index a6f07fc..a937b43 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -21,6 +21,7 @@
 #include <sys/unistd.h>
 
 #include <optional>
+#include <sstream>
 #include <thread>
 #include <unordered_set>
 
@@ -37,6 +38,10 @@
 #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 "device_info.h"
 #include "partition_cow_creator.h"
@@ -183,11 +188,19 @@
     return true;
 }
 
-SnapshotManager::Slot SnapshotManager::GetCurrentSlot() {
+std::string SnapshotManager::ReadUpdateSourceSlotSuffix() {
     auto boot_file = GetSnapshotBootIndicatorPath();
     std::string contents;
     if (!android::base::ReadFileToString(boot_file, &contents)) {
         PLOG(WARNING) << "Cannot read " << boot_file;
+        return {};
+    }
+    return contents;
+}
+
+SnapshotManager::Slot SnapshotManager::GetCurrentSlot() {
+    auto contents = ReadUpdateSourceSlotSuffix();
+    if (contents.empty()) {
         return Slot::Unknown;
     }
     if (device_->GetSlotSuffix() == contents) {
@@ -196,13 +209,44 @@
     return Slot::Target;
 }
 
+static bool RemoveFileIfExists(const std::string& path) {
+    std::string message;
+    if (!android::base::RemoveFileIfExists(path, &message)) {
+        LOG(ERROR) << "Remove failed: " << path << ": " << message;
+        return false;
+    }
+    return true;
+}
+
 bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) {
+    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";
+    android::base::WriteStringToFile(callstack_str.c_str(), path.str());
+#endif
+
     if (!RemoveAllSnapshots(lock)) {
         LOG(ERROR) << "Could not remove all snapshots";
         return false;
     }
 
-    RemoveSnapshotBootIndicator();
+    // It's okay if these fail - first-stage init performs a deeper check after
+    // reading the indicator file, so it's not a problem if it still exists
+    // after the update completes.
+    std::vector<std::string> files = {GetSnapshotBootIndicatorPath(), GetRollbackIndicatorPath()};
+    for (const auto& file : files) {
+        RemoveFileIfExists(file);
+    }
 
     // If this fails, we'll keep trying to remove the update state (as the
     // device reboots or starts a new update) until it finally succeeds.
@@ -229,6 +273,13 @@
         return false;
     }
 
+    // This file is written on boot to detect whether a rollback occurred. It
+    // MUST NOT exist before rebooting, otherwise, we're at risk of deleting
+    // snapshots too early.
+    if (!RemoveFileIfExists(GetRollbackIndicatorPath())) {
+        return false;
+    }
+
     // This file acts as both a quick indicator for init (it can use access(2)
     // to decide how to do first-stage mounts), and it stores the old slot, so
     // we can tell whether or not we performed a rollback.
@@ -945,14 +996,8 @@
     return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath);
 }
 
-void SnapshotManager::RemoveSnapshotBootIndicator() {
-    // It's okay if this fails - first-stage init performs a deeper check after
-    // reading the indicator file, so it's not a problem if it still exists
-    // after the update completes.
-    auto boot_file = GetSnapshotBootIndicatorPath();
-    if (unlink(boot_file.c_str()) == -1 && errno != ENOENT) {
-        PLOG(ERROR) << "unlink " << boot_file;
-    }
+std::string SnapshotManager::GetRollbackIndicatorPath() {
+    return metadata_dir_ + "/rollback-indicator";
 }
 
 void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
@@ -1123,25 +1168,18 @@
     if (slot == Slot::Unknown) {
         return false;
     }
-    if (slot == Slot::Target) {
-        // We're booted into the target slot, which means we just rebooted
-        // after applying the update.
-        if (!HandleCancelledUpdateOnNewSlot(lock)) {
-            return false;
-        }
+
+    // If all snapshots were reflashed, then cancel the entire update.
+    if (AreAllSnapshotsCancelled(lock)) {
+        RemoveAllUpdateState(lock);
+        return true;
     }
 
-    // The only way we can get here is if:
-    //  (1) The device rolled back to the previous slot.
-    //  (2) This function was called prematurely before rebooting the device.
-    //  (3) fastboot set_active was used.
-    //  (4) The device updates to the new slot but re-flashed *all* partitions
-    //      in the new slot.
-    //
-    // In any case, delete the snapshots. It may be worth using the boot_control
-    // HAL to differentiate case (2).
-    RemoveAllUpdateState(lock);
-    return true;
+    // This unverified update might be rolled back, or it might not (b/147347110
+    // comment #77). Take no action, as update_engine is responsible for deciding
+    // whether to cancel.
+    LOG(ERROR) << "Update state is being processed before reboot, taking no action.";
+    return false;
 }
 
 std::unique_ptr<LpMetadata> SnapshotManager::ReadCurrentMetadata() {
@@ -1166,7 +1204,7 @@
     return MetadataPartitionState::Flashed;
 }
 
-bool SnapshotManager::HandleCancelledUpdateOnNewSlot(LockedFile* lock) {
+bool SnapshotManager::AreAllSnapshotsCancelled(LockedFile* lock) {
     std::vector<std::string> snapshots;
     if (!ListSnapshots(lock, &snapshots)) {
         LOG(WARNING) << "Failed to list snapshots to determine whether device has been flashed "
@@ -1175,35 +1213,45 @@
         return true;
     }
 
+    auto source_slot_suffix = ReadUpdateSourceSlotSuffix();
+    if (source_slot_suffix.empty()) {
+        return false;
+    }
+    uint32_t source_slot = SlotNumberForSlotSuffix(source_slot_suffix);
+    uint32_t target_slot = (source_slot == 0) ? 1 : 0;
+
     // Attempt to detect re-flashing on each partition.
     // - If all partitions are re-flashed, we can proceed to cancel the whole update.
     // - If only some of the partitions are re-flashed, snapshots for re-flashed partitions are
     //   deleted. Caller is responsible for merging the rest of the snapshots.
     // - If none of the partitions are re-flashed, caller is responsible for merging the snapshots.
-    auto metadata = ReadCurrentMetadata();
-    if (!metadata) return false;
-    bool all_snapshot_cancelled = true;
+    //
+    // Note that we use target slot metadata, since if an OTA has been applied
+    // to the target slot, we can detect the UPDATED flag. Any kind of flash
+    // operation against dynamic partitions ensures that all copies of the
+    // metadata are in sync, so flashing all partitions on the source slot will
+    // remove the UPDATED flag on the target slot as well.
+    const auto& opener = device_->GetPartitionOpener();
+    auto super_device = device_->GetSuperDevice(target_slot);
+    auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, target_slot);
+    if (!metadata) {
+        return false;
+    }
+
+    bool all_snapshots_cancelled = true;
     for (const auto& snapshot_name : snapshots) {
         if (GetMetadataPartitionState(*metadata, snapshot_name) ==
             MetadataPartitionState::Updated) {
-            LOG(WARNING) << "Cannot cancel update because snapshot" << snapshot_name
-                         << " is in use.";
-            all_snapshot_cancelled = false;
+            all_snapshots_cancelled = false;
             continue;
         }
         // Delete snapshots for partitions that are re-flashed after the update.
-        LOG(INFO) << "Detected re-flashing of partition " << snapshot_name << ".";
-        if (!DeleteSnapshot(lock, snapshot_name)) {
-            // This is an error, but it is okay to leave the snapshot in the short term.
-            // However, if all_snapshot_cancelled == false after exiting the loop, caller may
-            // initiate merge for this unused snapshot, which is likely to fail.
-            LOG(WARNING) << "Failed to delete snapshot for re-flashed partition " << snapshot_name;
-        }
+        LOG(WARNING) << "Detected re-flashing of partition " << snapshot_name << ".";
     }
-    if (!all_snapshot_cancelled) return false;
-
-    LOG(INFO) << "All partitions are re-flashed after update, removing all update states.";
-    return true;
+    if (all_snapshots_cancelled) {
+        LOG(WARNING) << "All partitions are re-flashed after update, removing all update states.";
+    }
+    return all_snapshots_cancelled;
 }
 
 bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
@@ -1323,7 +1371,16 @@
     // the reason be clearer? Because the indicator file still exists, and
     // if this was FATAL, reverting to the old slot would be broken.
     auto slot = GetCurrentSlot();
+
     if (slot != Slot::Target) {
+        if (slot == Slot::Source && !device_->IsRecovery()) {
+            // Device is rebooting into the original slot, so mark this as a
+            // rollback.
+            auto path = GetRollbackIndicatorPath();
+            if (!android::base::WriteStringToFile("1", path)) {
+                PLOG(ERROR) << "Unable to write rollback indicator: " << path;
+            }
+        }
         LOG(INFO) << "Not booting from new slot. Will not mount snapshots.";
         return false;
     }
@@ -2349,6 +2406,10 @@
         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;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index c5ad44c..d87274d 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -447,6 +447,9 @@
     auto sm = SnapshotManager::NewForFirstStageMount(info);
     ASSERT_NE(sm, nullptr);
     ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());
+
+    auto indicator = sm->GetRollbackIndicatorPath();
+    ASSERT_EQ(access(indicator.c_str(), R_OK), 0);
 }
 
 TEST_F(SnapshotTest, Merge) {
@@ -503,6 +506,9 @@
 }
 
 TEST_F(SnapshotTest, FirstStageMountAndMerge) {
+#ifdef SKIP_TEST_IN_PRESUBMIT
+    GTEST_SKIP() << "WIP failure b/148889015";
+#endif
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
@@ -559,6 +565,9 @@
 }
 
 TEST_F(SnapshotTest, FlashSuperDuringMerge) {
+#ifdef SKIP_TEST_IN_PRESUBMIT
+    GTEST_SKIP() << "WIP failure b/148889015";
+#endif
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
@@ -970,6 +979,9 @@
 // Also test UnmapUpdateSnapshot unmaps everything.
 // Also test first stage mount and merge after this.
 TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
+#ifdef SKIP_TEST_IN_PRESUBMIT
+    GTEST_SKIP() << "WIP failure b/148889015";
+#endif
     // OTA client blindly unmaps all partitions that are possibly mapped.
     for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
         ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
@@ -1015,6 +1027,9 @@
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
 
+    auto indicator = sm->GetRollbackIndicatorPath();
+    ASSERT_NE(access(indicator.c_str(), R_OK), 0);
+
     // Check that the target partitions have the same content.
     for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
         ASSERT_TRUE(IsPartitionUnchanged(name));
@@ -1114,6 +1129,9 @@
 
 // Test that the old partitions are not modified.
 TEST_F(SnapshotUpdateTest, TestRollback) {
+#ifdef SKIP_TEST_IN_PRESUBMIT
+    GTEST_SKIP() << "WIP failure b/148889015";
+#endif
     // Execute the update.
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->UnmapUpdateSnapshot("sys_b"));
@@ -1291,6 +1309,9 @@
 }
 
 TEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) {
+#ifdef SKIP_TEST_IN_PRESUBMIT
+    GTEST_SKIP() << "WIP failure b/148889015";
+#endif
     // Make source partitions as big as possible to force COW image to be created.
     SetSize(sys_, 5_MiB);
     SetSize(vnd_, 5_MiB);
@@ -1565,6 +1586,9 @@
 }
 
 TEST_F(SnapshotUpdateTest, WaitForMerge) {
+#ifdef SKIP_TEST_IN_PRESUBMIT
+    GTEST_SKIP() << "WIP failure b/148889015";
+#endif
     AddOperationForPartitions();
 
     // Execute the update.
@@ -1681,9 +1705,11 @@
     ASSERT_NE(nullptr, flashed_builder->FindPartition("prd" + flashed_slot_suffix));
     flashed_builder->RemovePartition("prd" + flashed_slot_suffix);
 
+    // Note that fastbootd always updates the partition table of both slots.
     auto flashed_metadata = flashed_builder->Export();
     ASSERT_NE(nullptr, flashed_metadata);
-    ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *flashed_metadata, flashed_slot));
+    ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *flashed_metadata, 0));
+    ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *flashed_metadata, 1));
 
     std::string path;
     for (const auto& name : {"sys", "vnd"}) {
@@ -1782,6 +1808,10 @@
     std::vector<uint64_t> ret;
     for (uint64_t size = 1_MiB; size <= 512_MiB; size *= 2) {
         ret.push_back(size);
+#ifdef SKIP_TEST_IN_PRESUBMIT
+        // BUG(148889015);
+        break;
+#endif
     }
     return ret;
 }
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index a0d0c03..d724be3 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -27,6 +27,8 @@
 #include <libsnapshot/snapshot.h>
 #include "utility.h"
 
+#include "utility.h"
+
 using namespace std::string_literals;
 
 int Usage() {
@@ -110,7 +112,9 @@
 
     auto state = SnapshotManager::New()->InitiateMergeAndWait();
 
-    if (state == UpdateState::None) {
+    // 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;
     }
     if (state == UpdateState::MergeCompleted) {
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index d8c4843..21663e6 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -21,6 +21,7 @@
 #include <unistd.h>
 
 #include <chrono>
+#include <map>
 #include <memory>
 #include <set>
 #include <string>
@@ -29,6 +30,7 @@
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <fs_avb/fs_avb.h>
 #include <fs_mgr.h>
@@ -45,7 +47,9 @@
 #include "uevent_listener.h"
 #include "util.h"
 
+using android::base::ReadFileToString;
 using android::base::Split;
+using android::base::StringPrintf;
 using android::base::Timer;
 using android::fiemap::IImageManager;
 using android::fs_mgr::AvbHandle;
@@ -95,6 +99,7 @@
     void GetDmLinearMetadataDevice(std::set<std::string>* devices);
     bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
     void UseDsuIfPresent();
+    void PreloadAvbKeys();
 
     ListenerAction UeventCallback(const Uevent& uevent, std::set<std::string>* required_devices);
 
@@ -110,6 +115,9 @@
     std::string super_partition_name_;
     std::unique_ptr<DeviceHandler> device_handler_;
     UeventListener uevent_listener_;
+    // Reads all AVB keys before chroot into /system, as they might be used
+    // later when mounting other partitions, e.g., /vendor and /product.
+    std::map<std::string, std::vector<std::string>> preload_avb_key_blobs_;
 };
 
 class FirstStageMountVBootV1 : public FirstStageMount {
@@ -508,11 +516,57 @@
     return mounted;
 }
 
+void FirstStageMount::PreloadAvbKeys() {
+    for (const auto& entry : fstab_) {
+        // No need to cache the key content if it's empty, or is already cached.
+        if (entry.avb_keys.empty() || preload_avb_key_blobs_.count(entry.avb_keys)) {
+            continue;
+        }
+
+        // Determines all key paths first.
+        std::vector<std::string> key_paths;
+        if (is_dir(entry.avb_keys.c_str())) {  // fstab_keys might be a dir, e.g., /avb.
+            const char* avb_key_dir = entry.avb_keys.c_str();
+            std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(avb_key_dir), closedir);
+            if (!dir) {
+                LOG(ERROR) << "Failed to opendir: " << dir;
+                continue;
+            }
+            // Gets all key pathes under the dir.
+            struct dirent* de;
+            while ((de = readdir(dir.get()))) {
+                if (de->d_type != DT_REG) continue;
+                std::string full_path = StringPrintf("%s/%s", avb_key_dir, de->d_name);
+                key_paths.emplace_back(std::move(full_path));
+            }
+            std::sort(key_paths.begin(), key_paths.end());
+        } else {
+            // avb_keys are key paths separated by ":", if it's not a dir.
+            key_paths = Split(entry.avb_keys, ":");
+        }
+
+        // Reads the key content then cache it.
+        std::vector<std::string> key_blobs;
+        for (const auto& path : key_paths) {
+            std::string key_value;
+            if (!ReadFileToString(path, &key_value)) {
+                continue;
+            }
+            key_blobs.emplace_back(std::move(key_value));
+        }
+
+        // Maps entry.avb_keys to actual key blobs.
+        preload_avb_key_blobs_[entry.avb_keys] = std::move(key_blobs);
+    }
+}
+
 // If system is in the fstab then we're not a system-as-root device, and in
 // this case, we mount system first then pivot to it.  From that point on,
 // we are effectively identical to a system-as-root device.
 bool FirstStageMount::TrySwitchSystemAsRoot() {
     UseDsuIfPresent();
+    // Preloading all AVB keys from the ramdisk before switching root to /system.
+    PreloadAvbKeys();
 
     auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
         return entry.mount_point == "/system";
@@ -776,7 +830,8 @@
                        << fstab_entry->mount_point;
             return true;  // Returns true to mount the partition directly.
         } else {
-            auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(*fstab_entry);
+            auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(
+                    *fstab_entry, preload_avb_key_blobs_[fstab_entry->avb_keys]);
             if (!avb_standalone_handle) {
                 LOG(ERROR) << "Failed to load offline vbmeta for " << fstab_entry->mount_point;
                 // Fallbacks to built-in hashtree if fs_mgr_flags.avb is set.
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 1d431e3..3f81792 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -208,7 +208,7 @@
 
         // If the property is not set, it defaults to none, in which case there are no keycodes
         // for this service.
-        if (expanded == "none") {
+        if (*expanded == "none") {
             return {};
         }
 
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
index a0c526b..4937042 100644
--- a/liblog/logger_read.cpp
+++ b/liblog/logger_read.cpp
@@ -109,8 +109,8 @@
     return ret;
   }
 
-  if (ret > (int)sizeof(*log_msg)) {
-    ret = sizeof(*log_msg);
+  if (ret > LOGGER_ENTRY_MAX_LEN) {
+    ret = LOGGER_ENTRY_MAX_LEN;
   }
 
   if (ret < static_cast<int>(sizeof(log_msg->entry))) {
@@ -118,7 +118,7 @@
   }
 
   if (log_msg->entry.hdr_size < sizeof(log_msg->entry) ||
-      log_msg->entry.hdr_size >= sizeof(struct log_msg) - sizeof(log_msg->entry)) {
+      log_msg->entry.hdr_size >= LOGGER_ENTRY_MAX_LEN - sizeof(log_msg->entry)) {
     return -EINVAL;
   }
 
@@ -126,6 +126,8 @@
     return -EINVAL;
   }
 
+  log_msg->buf[log_msg->entry.len + log_msg->entry.hdr_size] = '\0';
+
   return ret;
 }
 
diff --git a/rootdir/init.rc b/rootdir/init.rc
index c6fcafa..5fbad75 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -694,10 +694,6 @@
     mkdir /data/user/0 0700 system system encryption=None
     mount none /data/data /data/user/0 bind rec
 
-    # Special-case /data/media/obb per b/64566063
-    mkdir /data/media 0770 media_rw media_rw encryption=None
-    mkdir /data/media/obb 0770 media_rw media_rw encryption=Attempt
-
     # A tmpfs directory, which will contain all apps CE DE data directory that
     # bind mount from the original source.
     chown root root /data_mirror
@@ -735,6 +731,11 @@
     wait_for_prop apexd.status ready
     perform_apex_config
 
+    # Special-case /data/media/obb per b/64566063
+    mkdir /data/media 0770 media_rw media_rw encryption=None
+    exec - media_rw media_rw -- /system/bin/chattr +F /data/media
+    mkdir /data/media/obb 0770 media_rw media_rw encryption=Attempt
+
     exec_start derive_sdk
 
     init_user0