Merge "Refactor update status management as protobuf"
am: 5c63dc9322

Change-Id: I9a995365cbbf354a8f3da09f1d900d9e52b5e4a9
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index eadcecc..9e67c66 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -127,6 +127,7 @@
         "include_test",
     ],
     srcs: [
+        "android/snapshot/snapshot.proto",
         "test_helpers.cpp",
     ],
     shared_libs: [
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 629c3a4..a3a518d 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -85,3 +85,49 @@
     // This is non-zero when |state| == MERGING or MERGE_COMPLETED.
     uint64 metadata_sectors = 8;
 }
+
+// Next: 8
+enum UpdateState {
+    // No update or merge is in progress.
+    None = 0;
+
+    // An update is applying; snapshots may already exist.
+    Initiated = 1;
+
+    // An update is pending, but has not been successfully booted yet.
+    Unverified = 2;
+
+    // The kernel is merging in the background.
+    Merging = 3;
+
+    // Post-merge cleanup steps could not be completed due to a transient
+    // error, but the next reboot will finish any pending operations.
+    MergeNeedsReboot = 4;
+
+    // Merging is complete, and needs to be acknowledged.
+    MergeCompleted = 5;
+
+    // Merging failed due to an unrecoverable error.
+    MergeFailed = 6;
+
+    // The update was implicitly cancelled, either by a rollback or a flash
+    // operation via fastboot. This state can only be returned by WaitForMerge.
+    Cancelled = 7;
+};
+
+// Next: 5
+message SnapshotUpdateStatus {
+    UpdateState state = 1;
+
+    // Total number of sectors allocated in the COW files before performing the
+    // merge operation.  This field is used to keep track of the total number
+    // of sectors modified to monitor and show the progress of the merge during
+    // an update.
+    uint64 sectors_allocated = 2;
+
+    // Total number of sectors of all the snapshot devices.
+    uint64 total_sectors = 3;
+
+    // Sectors allocated for metadata in all the snapshot devices.
+    uint64 metadata_sectors = 4;
+}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 6e613ba..61946f7 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -26,6 +26,7 @@
 #include <vector>
 
 #include <android-base/unique_fd.h>
+#include <android/snapshot/snapshot.pb.h>
 #include <fs_mgr_dm_linear.h>
 #include <libdm/dm.h>
 #include <libfiemap/image_manager.h>
@@ -80,35 +81,6 @@
     NOT_CREATED,
 };
 
-enum class UpdateState : unsigned int {
-    // No update or merge is in progress.
-    None,
-
-    // An update is applying; snapshots may already exist.
-    Initiated,
-
-    // An update is pending, but has not been successfully booted yet.
-    Unverified,
-
-    // The kernel is merging in the background.
-    Merging,
-
-    // Post-merge cleanup steps could not be completed due to a transient
-    // error, but the next reboot will finish any pending operations.
-    MergeNeedsReboot,
-
-    // Merging is complete, and needs to be acknowledged.
-    MergeCompleted,
-
-    // Merging failed due to an unrecoverable error.
-    MergeFailed,
-
-    // The update was implicitly cancelled, either by a rollback or a flash
-    // 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;
     using IPartitionOpener = android::fs_mgr::IPartitionOpener;
@@ -433,7 +405,9 @@
 
     // Interact with /metadata/ota/state.
     UpdateState ReadUpdateState(LockedFile* file);
+    SnapshotUpdateStatus ReadSnapshotUpdateStatus(LockedFile* file);
     bool WriteUpdateState(LockedFile* file, UpdateState state);
+    bool WriteSnapshotUpdateStatus(LockedFile* file, const SnapshotUpdateStatus& status);
     std::string GetStateFilePath() const;
 
     // Helpers for merging.
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index b79b65c..70a69a5 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -560,9 +560,26 @@
         }
     }
 
+    DmTargetSnapshot::Status initial_target_values = {};
+    for (const auto& snapshot : snapshots) {
+        DmTargetSnapshot::Status current_status;
+        if (!QuerySnapshotStatus(snapshot, nullptr, &current_status)) {
+            return false;
+        }
+        initial_target_values.sectors_allocated += current_status.sectors_allocated;
+        initial_target_values.total_sectors += current_status.total_sectors;
+        initial_target_values.metadata_sectors += current_status.metadata_sectors;
+    }
+
+    SnapshotUpdateStatus initial_status;
+    initial_status.set_state(UpdateState::Merging);
+    initial_status.set_sectors_allocated(initial_target_values.sectors_allocated);
+    initial_status.set_total_sectors(initial_target_values.total_sectors);
+    initial_status.set_metadata_sectors(initial_target_values.metadata_sectors);
+
     // Point of no return - mark that we're starting a merge. From now on every
     // snapshot must be a merge target.
-    if (!WriteUpdateState(lock.get(), UpdateState::Merging)) {
+    if (!WriteSnapshotUpdateStatus(lock.get(), initial_status)) {
         return false;
     }
 
@@ -1643,15 +1660,7 @@
     return OpenLock(LOCK_EX);
 }
 
-UpdateState SnapshotManager::ReadUpdateState(LockedFile* lock) {
-    CHECK(lock);
-
-    std::string contents;
-    if (!android::base::ReadFileToString(GetStateFilePath(), &contents)) {
-        PLOG(ERROR) << "Read state file failed";
-        return UpdateState::None;
-    }
-
+static UpdateState UpdateStateFromString(const std::string& contents) {
     if (contents.empty() || contents == "none") {
         return UpdateState::None;
     } else if (contents == "initiated") {
@@ -1694,18 +1703,54 @@
     }
 }
 
+UpdateState SnapshotManager::ReadUpdateState(LockedFile* lock) {
+    SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock);
+    return status.state();
+}
+
+SnapshotUpdateStatus SnapshotManager::ReadSnapshotUpdateStatus(LockedFile* lock) {
+    CHECK(lock);
+
+    SnapshotUpdateStatus status = {};
+    std::string contents;
+    if (!android::base::ReadFileToString(GetStateFilePath(), &contents)) {
+        PLOG(ERROR) << "Read state file failed";
+        status.set_state(UpdateState::None);
+        return status;
+    }
+
+    if (!status.ParseFromString(contents)) {
+        LOG(WARNING) << "Unable to parse state file as SnapshotUpdateStatus, using the old format";
+
+        // Try to rollback to legacy file to support devices that are
+        // currently using the old file format.
+        // TODO(b/147409432)
+        status.set_state(UpdateStateFromString(contents));
+    }
+
+    return status;
+}
+
 bool SnapshotManager::WriteUpdateState(LockedFile* lock, UpdateState state) {
+    SnapshotUpdateStatus status = {};
+    status.set_state(state);
+    return WriteSnapshotUpdateStatus(lock, status);
+}
+
+bool SnapshotManager::WriteSnapshotUpdateStatus(LockedFile* lock,
+                                                const SnapshotUpdateStatus& status) {
     CHECK(lock);
     CHECK(lock->lock_mode() == LOCK_EX);
 
-    std::stringstream ss;
-    ss << state;
-    std::string contents = ss.str();
-    if (contents.empty()) return false;
+    std::string contents;
+    if (!status.SerializeToString(&contents)) {
+        LOG(ERROR) << "Unable to serialize SnapshotUpdateStatus.";
+        return false;
+    }
 
 #ifdef LIBSNAPSHOT_USE_HAL
     auto merge_status = MergeStatus::UNKNOWN;
-    switch (state) {
+    switch (status.state()) {
         // The needs-reboot and completed cases imply that /data and /metadata
         // can be safely wiped, so we don't report a merge status.
         case UpdateState::None:
@@ -1724,7 +1769,7 @@
         default:
             // Note that Cancelled flows to here - it is never written, since
             // it only communicates a transient state to the caller.
-            LOG(ERROR) << "Unexpected update status: " << state;
+            LOG(ERROR) << "Unexpected update status: " << status.state();
             break;
     }