[fuchsia] Flush LegacyMetricsClient buffer for OnCloseSoon FIDL event.

Handles the fuchsia.legacymetrics.MetricsRecorder OnCloseSoon() event.
It is sent by the service to request that its clients deliver any
buffered metrics data prior to an impending orderly shutdown.

Bug: 1087971
Change-Id: I5eb76bb2362dc372890cdea32e4a56a77265951e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2225578
Commit-Queue: Kevin Marshall <[email protected]>
Reviewed-by: Sergey Ulanov <[email protected]>
Cr-Commit-Position: refs/heads/master@{#775342}
diff --git a/fuchsia/base/legacymetrics_client.cc b/fuchsia/base/legacymetrics_client.cc
index a2d45a83..c8ad92e 100644
--- a/fuchsia/base/legacymetrics_client.cc
+++ b/fuchsia/base/legacymetrics_client.cc
@@ -39,6 +39,8 @@
                           ->Connect<fuchsia::legacymetrics::MetricsRecorder>();
   metrics_recorder_.set_error_handler(fit::bind_member(
       this, &LegacyMetricsClient::OnMetricsRecorderDisconnected));
+  metrics_recorder_.events().OnCloseSoon =
+      fit::bind_member(this, &LegacyMetricsClient::OnCloseSoon);
   user_events_recorder_ = std::make_unique<LegacyMetricsUserActionRecorder>();
   ScheduleNextReport();
 }
@@ -54,7 +56,18 @@
   report_additional_callback_ = std::move(callback);
 }
 
+void LegacyMetricsClient::SetNotifyFlushCallback(NotifyFlushCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(callback);
+  DCHECK(!metrics_recorder_)
+      << "SetNotifyFlushCallback() must be called before Start().";
+
+  notify_flush_callback_ = std::move(callback);
+}
+
 void LegacyMetricsClient::ScheduleNextReport() {
+  DCHECK(!is_flushing_);
+
   DVLOG(1) << "Scheduling next report in " << report_interval_.InSeconds()
            << "seconds.";
   timer_.Start(FROM_HERE, report_interval_, this,
@@ -91,35 +104,46 @@
     }
   }
 
-  if (events.empty()) {
-    ScheduleNextReport();
-    return;
-  }
+  std::move(events.begin(), events.end(), std::back_inserter(to_send_));
 
-  DrainBuffer(std::move(events));
+  DrainBuffer();
 }
 
-void LegacyMetricsClient::DrainBuffer(
-    std::vector<fuchsia::legacymetrics::Event> buffer) {
-  if (buffer.empty()) {
-    DVLOG(1) << "Buffer drained.";
-    ScheduleNextReport();
+void LegacyMetricsClient::DrainBuffer() {
+  DVLOG(1) << __func__ << " called.";
+
+  if (record_ack_pending_) {
+    // There is a Record() call already inflight. When it is acknowledged,
+    // buffer draining will continue.
     return;
   }
 
-  // Since ordering doesn't matter, we can efficiently drain |buffer| by
+  if (to_send_.empty()) {
+    DVLOG(1) << "Buffer drained.";
+
+    if (is_flushing_) {
+      metrics_recorder_.Unbind();
+    } else {
+      ScheduleNextReport();
+    }
+
+    return;
+  }
+
+  // Since ordering doesn't matter, we can efficiently drain |to_send_| by
   // repeatedly sending and truncating its tail.
-  const size_t batch_size = std::min(buffer.size(), kMaxBatchSize);
-  const size_t batch_start_idx = buffer.size() - batch_size;
+  const size_t batch_size = std::min(to_send_.size(), kMaxBatchSize);
+  const size_t batch_start_idx = to_send_.size() - batch_size;
   std::vector<fuchsia::legacymetrics::Event> batch;
   batch.resize(batch_size);
-  std::move(buffer.begin() + batch_start_idx, buffer.end(), batch.begin());
-  buffer.resize(buffer.size() - batch_size);
+  std::move(to_send_.begin() + batch_start_idx, to_send_.end(), batch.begin());
+  to_send_.resize(to_send_.size() - batch_size);
 
-  metrics_recorder_->Record(std::move(batch),
-                            [this, buffer = std::move(buffer)]() mutable {
-                              DrainBuffer(std::move(buffer));
-                            });
+  record_ack_pending_ = true;
+  metrics_recorder_->Record(std::move(batch), [this]() {
+    record_ack_pending_ = false;
+    DrainBuffer();
+  });
 }
 
 void LegacyMetricsClient::OnMetricsRecorderDisconnected(zx_status_t status) {
@@ -131,4 +155,20 @@
   timer_.AbandonAndStop();
 }
 
+void LegacyMetricsClient::OnCloseSoon() {
+  DVLOG(1) << __func__ << " called.";
+
+  timer_.AbandonAndStop();
+
+  is_flushing_ = true;
+  if (notify_flush_callback_) {
+    // Defer reporting until the flush operation has finished.
+    std::move(notify_flush_callback_)
+        .Run(base::BindOnce(&LegacyMetricsClient::StartReport,
+                            weak_factory_.GetWeakPtr()));
+  } else {
+    StartReport();
+  }
+}
+
 }  // namespace cr_fuchsia