Add metrics to record observation lifetime in the buffer

To understand the lifetime of the observations, let me record its
lifetime.

Bug: 351092743
Change-Id: Ie334da859d7b2436e2898c281a77584d09667517
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5678314
Commit-Queue: Yoshisato Yanagisawa <yyanagisawa@chromium.org>
Reviewed-by: Adam Rice <ricea@chromium.org>
Reviewed-by: Keita Suzuki <suzukikeita@chromium.org>
Reviewed-by: Kouhei Ueno <kouhei@chromium.org>
Reviewed-by: Minoru Chikamune <chikamune@chromium.org>
Reviewed-by: Shunya Shishido <sisidovski@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1324085}
diff --git a/net/nqe/network_quality_estimator.cc b/net/nqe/network_quality_estimator.cc
index a5b6575c..03d5530 100644
--- a/net/nqe/network_quality_estimator.cc
+++ b/net/nqe/network_quality_estimator.cc
@@ -84,11 +84,24 @@
 
 nqe::internal::NetworkID DoGetCurrentNetworkID(
     NetworkQualityEstimatorParams* params) {
-    nqe::internal::NetworkID network_id(
-        NetworkChangeNotifier::GetConnectionType(), std::string(), INT32_MIN);
+  nqe::internal::NetworkID network_id(
+      NetworkChangeNotifier::GetConnectionType(), std::string(), INT32_MIN);
 
-      return network_id;
+  return network_id;
+}
+
+const char* CategoryToString(nqe::internal::ObservationCategory category) {
+  switch (category) {
+    case nqe::internal::OBSERVATION_CATEGORY_HTTP:
+      return "HTTP";
+    case nqe::internal::OBSERVATION_CATEGORY_TRANSPORT:
+      return "Transport";
+    case nqe::internal::OBSERVATION_CATEGORY_END_TO_END:
+      return "EndToEnd";
+    case nqe::internal::OBSERVATION_CATEGORY_COUNT:
+      NOTREACHED_NORETURN();
   }
+}
 
 }  // namespace
 
@@ -1098,7 +1111,17 @@
       observation.GetObservationCategories();
   for (nqe::internal::ObservationCategory observation_category :
        observation_categories) {
-    rtt_ms_observations_[observation_category].AddObservation(observation);
+    auto evicted =
+        rtt_ms_observations_[observation_category].AddObservation(observation);
+    if (evicted) {
+      auto delta = base::TimeTicks::Now() - evicted->timestamp();
+      base::UmaHistogramMediumTimes(
+          base::StrCat({"NQE.RTT.ObservationBufferLifeTime.",
+                        CategoryToString(observation_category)}),
+          delta);
+      base::UmaHistogramMediumTimes("NQE.RTT.ObservationBufferLifeTime.All",
+                                    delta);
+    }
   }
 
   if (observation.source() == NETWORK_QUALITY_OBSERVATION_SOURCE_TCP ||
diff --git a/net/nqe/observation_buffer.cc b/net/nqe/observation_buffer.cc
index 1e14892..11028fa 100644
--- a/net/nqe/observation_buffer.cc
+++ b/net/nqe/observation_buffer.cc
@@ -50,7 +50,8 @@
 
 ObservationBuffer::~ObservationBuffer() = default;
 
-void ObservationBuffer::AddObservation(const Observation& observation) {
+std::optional<Observation> ObservationBuffer::AddObservation(
+    const Observation& observation) {
   DCHECK_LE(observations_.size(), params_->observation_buffer_size());
 
   // Observations must be in the non-decreasing order of the timestamps.
@@ -61,12 +62,16 @@
          (observation.signal_strength() >= 0 &&
           observation.signal_strength() <= 4));
 
+  std::optional<Observation> evicted_observation;
   // Evict the oldest element if the buffer is already full.
-  if (observations_.size() == params_->observation_buffer_size())
+  if (observations_.size() == params_->observation_buffer_size()) {
+    evicted_observation = observations_.front();
     observations_.pop_front();
+  }
 
   observations_.push_back(observation);
   DCHECK_LE(observations_.size(), params_->observation_buffer_size());
+  return evicted_observation;
 }
 
 std::optional<int32_t> ObservationBuffer::GetPercentile(
diff --git a/net/nqe/observation_buffer.h b/net/nqe/observation_buffer.h
index 5692c58..bd32b91 100644
--- a/net/nqe/observation_buffer.h
+++ b/net/nqe/observation_buffer.h
@@ -55,7 +55,8 @@
 
   // Adds |observation| to the buffer. The oldest observation in the buffer
   // will be evicted to make room if the buffer is already full.
-  void AddObservation(const Observation& observation);
+  // In that case, an evicted observation will be returned.
+  std::optional<Observation> AddObservation(const Observation& observation);
 
   // Returns the number of observations in this buffer.
   size_t Size() const { return static_cast<size_t>(observations_.size()); }
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index c449d00..9f4d732 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -6103,6 +6103,32 @@
   </summary>
 </histogram>
 
+<histogram name="NQE.RTT.ObservationBufferLifeTime.{Category}" units="ms"
+    expires_after="2025-02-01">
+  <owner>yyanagisawa@chromium.org</owner>
+  <owner>chrome-loading@google.com</owner>
+  <summary>
+    NQE's estimated RTT comes from weighted medium value in an
+    ObservationBuffer. This is a metrics for {Category}. This metrics tells the
+    duration on how long an observation is kept in the ObservationBuffer so that
+    we can understand how fast the new status will be reflected. Recorded when
+    an observation is added to the buffer and an old observation gets evicted.
+  </summary>
+  <token key="Category">
+    <variant name="All"
+        summary="All ObservationBuffer (&quot;All&quot; represents sum of all
+                 categories)"/>
+    <variant name="EndToEnd"
+        summary="EndToEnd ObservationBuffer (lifetime of the end to end
+                 category)"/>
+    <variant name="HTTP"
+        summary="HTTP ObservationBuffer (lifetime of the HTTP category)"/>
+    <variant name="Transport"
+        summary="Transport ObservationBuffer (lifetime of the transport
+                 category)"/>
+  </token>
+</histogram>
+
 <histogram name="NQE.RTT.ObservationSource" enum="NQEObservationSource"
     expires_after="2024-10-01">
   <owner>yyanagisawa@chromium.org</owner>