Merge changes from topic "libsnapshot_api_update_engine" am: e71efc3dc4
am: f471637e19

Change-Id: I3bc16a14cb161d4f860da8cc3a8d618db0b917d3
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index aeeb4aa..6130a10 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -19,6 +19,7 @@
 #include <chrono>
 #include <map>
 #include <memory>
+#include <ostream>
 #include <string>
 #include <string_view>
 #include <vector>
@@ -84,6 +85,7 @@
     // operation via fastboot. This state can only be returned by WaitForMerge.
     Cancelled
 };
+std::ostream& operator<<(std::ostream& os, UpdateState state);
 
 class SnapshotManager final {
     using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
@@ -125,8 +127,9 @@
     // will fail if GetUpdateState() != None.
     bool BeginUpdate();
 
-    // Cancel an update; any snapshots will be deleted. This will fail if the
-    // state != Initiated or None.
+    // Cancel an update; any snapshots will be deleted. This is allowed if the
+    // state == Initiated, None, or Unverified (before rebooting to the new
+    // slot).
     bool CancelUpdate();
 
     // Mark snapshot writes as having completed. After this, new snapshots cannot
@@ -393,6 +396,13 @@
     // The reverse of MapPartitionWithSnapshot.
     bool UnmapPartitionWithSnapshot(LockedFile* lock, const std::string& target_partition_name);
 
+    // If there isn't a previous update, return true. |needs_merge| is set to false.
+    // If there is a previous update but the device has not boot into it, tries to cancel the
+    //   update and delete any snapshots. Return true if successful. |needs_merge| is set to false.
+    // If there is a previous update and the device has boot into it, do nothing and return true.
+    //   |needs_merge| is set to true.
+    bool TryCancelUpdate(bool* needs_merge);
+
     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 b5b3af3..803bdb5 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -131,6 +131,16 @@
 }
 
 bool SnapshotManager::BeginUpdate() {
+    bool needs_merge = false;
+    if (!TryCancelUpdate(&needs_merge)) {
+        return false;
+    }
+    if (needs_merge) {
+        LOG(INFO) << "Wait for merge (if any) before beginning a new update.";
+        auto state = ProcessUpdateState();
+        LOG(INFO) << "Merged with state = " << state;
+    }
+
     auto file = LockExclusive();
     if (!file) return false;
 
@@ -143,6 +153,19 @@
 }
 
 bool SnapshotManager::CancelUpdate() {
+    bool needs_merge = false;
+    if (!TryCancelUpdate(&needs_merge)) {
+        return false;
+    }
+    if (needs_merge) {
+        LOG(ERROR) << "Cannot cancel update after it has completed or started merging";
+    }
+    return !needs_merge;
+}
+
+bool SnapshotManager::TryCancelUpdate(bool* needs_merge) {
+    *needs_merge = false;
+
     auto file = LockExclusive();
     if (!file) return false;
 
@@ -167,8 +190,8 @@
             return RemoveAllUpdateState(file.get());
         }
     }
-    LOG(ERROR) << "Cannot cancel update after it has completed or started merging";
-    return false;
+    *needs_merge = true;
+    return true;
 }
 
 bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) {
@@ -1526,34 +1549,33 @@
     }
 }
 
-bool SnapshotManager::WriteUpdateState(LockedFile* file, UpdateState state) {
-    std::string contents;
+std::ostream& operator<<(std::ostream& os, UpdateState state) {
     switch (state) {
         case UpdateState::None:
-            contents = "none";
-            break;
+            return os << "none";
         case UpdateState::Initiated:
-            contents = "initiated";
-            break;
+            return os << "initiated";
         case UpdateState::Unverified:
-            contents = "unverified";
-            break;
+            return os << "unverified";
         case UpdateState::Merging:
-            contents = "merging";
-            break;
+            return os << "merging";
         case UpdateState::MergeCompleted:
-            contents = "merge-completed";
-            break;
+            return os << "merge-completed";
         case UpdateState::MergeNeedsReboot:
-            contents = "merge-needs-reboot";
-            break;
+            return os << "merge-needs-reboot";
         case UpdateState::MergeFailed:
-            contents = "merge-failed";
-            break;
+            return os << "merge-failed";
         default:
             LOG(ERROR) << "Unknown update state";
-            return false;
+            return os;
     }
+}
+
+bool SnapshotManager::WriteUpdateState(LockedFile* file, UpdateState state) {
+    std::stringstream ss;
+    ss << state;
+    std::string contents = ss.str();
+    if (contents.empty()) return false;
 
     if (!Truncate(file)) return false;
     if (!android::base::WriteStringToFd(contents, file->fd())) {
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 36982a9..bc764fd 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -761,8 +761,7 @@
 // Also test UnmapUpdateSnapshot unmaps everything.
 // Also test first stage mount and merge after this.
 TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
-    // OTA client calls CancelUpdate then BeginUpdate before doing anything.
-    ASSERT_TRUE(sm->CancelUpdate());
+    // OTA client calls BeginUpdate before doing anything.
     ASSERT_TRUE(sm->BeginUpdate());
 
     // OTA client blindly unmaps all partitions that are possibly mapped.