Merge "Snapshot merge progress percentage" am: 3ddd4304f5 am: e0e4df8486 am: bdd701400f

Change-Id: I19e10b6ee077206ac408d90603345c0bde63162f
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 81ffcaf..c731cb0 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -1249,15 +1249,45 @@
         return UpdateState::None;
     }
 
-    auto state = ReadUpdateState(lock.get());
-    if (progress) {
-        *progress = 0.0;
-        if (state == UpdateState::Merging) {
-            // :TODO: When merging is implemented, set progress_val.
-        } else if (state == UpdateState::MergeCompleted) {
-            *progress = 100.0;
-        }
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
+    auto state = update_status.state();
+    if (progress == nullptr) {
+        return state;
     }
+
+    if (state == UpdateState::MergeCompleted) {
+        *progress = 100.0;
+        return state;
+    }
+
+    *progress = 0.0;
+    if (state != UpdateState::Merging) {
+        return state;
+    }
+
+    // Sum all the snapshot states as if the system consists of a single huge
+    // snapshots device, then compute the merge completion percentage of that
+    // device.
+    std::vector<std::string> snapshots;
+    if (!ListSnapshots(lock.get(), &snapshots)) {
+        LOG(ERROR) << "Could not list snapshots";
+        return state;
+    }
+
+    DmTargetSnapshot::Status fake_snapshots_status = {};
+    for (const auto& snapshot : snapshots) {
+        DmTargetSnapshot::Status current_status;
+
+        if (!QuerySnapshotStatus(snapshot, nullptr, &current_status)) continue;
+
+        fake_snapshots_status.sectors_allocated += current_status.sectors_allocated;
+        fake_snapshots_status.total_sectors += current_status.total_sectors;
+        fake_snapshots_status.metadata_sectors += current_status.metadata_sectors;
+    }
+
+    *progress = DmTargetSnapshot::MergePercent(fake_snapshots_status,
+                                               update_status.sectors_allocated());
+
     return state;
 }
 
@@ -2284,9 +2314,19 @@
         }
     }
 
+    unsigned int last_progress = 0;
+    auto callback = [&]() -> void {
+        double progress;
+        GetUpdateState(&progress);
+        if (last_progress < static_cast<unsigned int>(progress)) {
+            last_progress = progress;
+            LOG(INFO) << "Waiting for merge to complete: " << last_progress << "%.";
+        }
+    };
+
     LOG(INFO) << "Waiting for any previous merge request to complete. "
               << "This can take up to several minutes.";
-    auto state = ProcessUpdateState();
+    auto state = ProcessUpdateState(callback);
     if (state == UpdateState::None) {
         LOG(INFO) << "Can't find any snapshot to merge.";
         return state;
@@ -2298,7 +2338,8 @@
         }
         // All other states can be handled by ProcessUpdateState.
         LOG(INFO) << "Waiting for merge to complete. This can take up to several minutes.";
-        state = ProcessUpdateState();
+        last_progress = 0;
+        state = ProcessUpdateState(callback);
     }
 
     LOG(INFO) << "Merge finished with state \"" << state << "\".";