Collect android metrics for bytes downloaded

Report bytes downloaded for update attempts and successful updates.

Prefs used to help metrics report:
kPrefsCurrentBytesDownloaded
kPrefsTotalBytesDownloaded

Bug: 30989466
Test: unittest pass
Change-Id: I7d213204ee2757551ad914c122a274965dfbff06
diff --git a/common/test_utils.h b/common/test_utils.h
index ba9f5f2..ddb3d34 100644
--- a/common/test_utils.h
+++ b/common/test_utils.h
@@ -31,6 +31,7 @@
 #include <base/callback.h>
 #include <base/files/file_path.h>
 #include <base/files/scoped_temp_dir.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include "update_engine/common/action.h"
@@ -101,6 +102,11 @@
 
 void FillWithData(brillo::Blob* buffer);
 
+// Compare the value of native array for download source parameter.
+MATCHER_P(DownloadSourceMatcher, source_array, "") {
+  return std::equal(source_array, source_array + kNumDownloadSources, arg);
+}
+
 // Class to unmount FS when object goes out of scope
 class ScopedFilesystemUnmounter {
  public:
diff --git a/payload_state_unittest.cc b/payload_state_unittest.cc
index c47e389..e469d2f 100644
--- a/payload_state_unittest.cc
+++ b/payload_state_unittest.cc
@@ -103,11 +103,6 @@
   EXPECT_EQ(expected_response_sign, stored_response_sign);
 }
 
-// Compare the value of native array for download source parameter.
-MATCHER_P(DownloadSourceMatcher, source_array, "") {
-  return memcmp(source_array, arg, kNumDownloadSources) == 0;
-}
-
 class PayloadStateTest : public ::testing::Test { };
 
 TEST(PayloadStateTest, SetResponseWorksWithEmptyResponse) {
@@ -917,7 +912,15 @@
 
   EXPECT_CALL(*fake_system_state.mock_metrics_reporter(),
               ReportSuccessfulUpdateMetrics(
-                  _, _, _, _, DownloadSourceMatcher(total_bytes), _, _, _, _))
+                  _,
+                  _,
+                  _,
+                  _,
+                  test_utils::DownloadSourceMatcher(total_bytes),
+                  _,
+                  _,
+                  _,
+                  _))
       .Times(1);
 
   payload_state.UpdateSucceeded();
diff --git a/update_attempter_android.cc b/update_attempter_android.cc
index 6d67000..7dcbeec 100644
--- a/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -372,6 +372,16 @@
   } else {
     ProgressUpdate(progress);
   }
+
+  // Update the bytes downloaded in prefs.
+  int64_t current_bytes_downloaded =
+      metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, prefs_);
+  int64_t total_bytes_downloaded =
+      metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, prefs_);
+  prefs_->SetInt64(kPrefsCurrentBytesDownloaded,
+                   current_bytes_downloaded + bytes_progressed);
+  prefs_->SetInt64(kPrefsTotalBytesDownloaded,
+                   total_bytes_downloaded + bytes_progressed);
 }
 
 bool UpdateAttempterAndroid::ShouldCancel(ErrorCode* cancel_reason) {
@@ -446,6 +456,8 @@
   ClearMetricsPrefs();
   if (error_code == ErrorCode::kSuccess) {
     metrics_utils::SetSystemUpdatedMarker(clock_.get(), prefs_);
+    // Clear the total bytes downloaded if and only if the update succeeds.
+    prefs_->SetInt64(kPrefsTotalBytesDownloaded, 0);
   }
 }
 
@@ -570,18 +582,43 @@
       attempt_result,
       error_code);
 
+  int64_t current_bytes_downloaded =
+      metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, prefs_);
+  metrics_reporter_->ReportUpdateAttemptDownloadMetrics(
+      current_bytes_downloaded,
+      0,
+      DownloadSource::kNumDownloadSources,
+      metrics::DownloadErrorCode::kUnset,
+      metrics::ConnectionType::kUnset);
+
   if (error_code == ErrorCode::kSuccess) {
     int64_t reboot_count =
         metrics_utils::GetPersistedValue(kPrefsNumReboots, prefs_);
     string build_version;
     prefs_->GetString(kPrefsPreviousVersion, &build_version);
+
+    // For android metrics, we only care about the total bytes downloaded
+    // for all sources; for now we assume the only download source is
+    // HttpsServer.
+    int64_t total_bytes_downloaded =
+        metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, prefs_);
+    int64_t num_bytes_downloaded[kNumDownloadSources] = {};
+    num_bytes_downloaded[DownloadSource::kDownloadSourceHttpsServer] =
+        total_bytes_downloaded;
+
+    int download_overhead_percentage = 0;
+    if (current_bytes_downloaded > 0) {
+      download_overhead_percentage =
+          (total_bytes_downloaded - current_bytes_downloaded) * 100ull /
+          current_bytes_downloaded;
+    }
     metrics_reporter_->ReportSuccessfulUpdateMetrics(
         static_cast<int>(attempt_number),
         0,  // update abandoned count
         payload_type,
         payload_size,
-        nullptr,  // num bytes downloaded
-        0,        // download overhead percentage
+        num_bytes_downloaded,
+        download_overhead_percentage,
         duration,
         static_cast<int>(reboot_count),
         0);  // url_switch_count
@@ -657,6 +694,7 @@
 
 void UpdateAttempterAndroid::ClearMetricsPrefs() {
   CHECK(prefs_);
+  prefs_->Delete(kPrefsCurrentBytesDownloaded);
   prefs_->Delete(kPrefsNumReboots);
   prefs_->Delete(kPrefsPayloadAttemptNumber);
   prefs_->Delete(kPrefsSystemUpdatedMarker);
diff --git a/update_attempter_android.h b/update_attempter_android.h
index 911ab81..28bf90a 100644
--- a/update_attempter_android.h
+++ b/update_attempter_android.h
@@ -128,6 +128,10 @@
   // |KprefsNumReboots|: number of reboots when applying the current update.
   // |kPrefsSystemUpdatedMarker|: end timestamp of the last successful update.
   // |kPrefsUpdateTimestampStart|: start timestamp of the current update.
+  // |kPrefsCurrentBytesDownloaded|: number of bytes downloaded for the current
+  // payload_id.
+  // |kPrefsTotalBytesDownloaded|: number of bytes downloaded in total since
+  // the last successful update.
 
   // Metrics report function to call:
   //   |ReportUpdateAttemptMetrics|
@@ -149,7 +153,8 @@
 
   // Prefs to delete:
   //   |kPrefsNumReboots|, |kPrefsPayloadAttemptNumber|,
-  //   |kPrefsSystemUpdatedMarker|, |kPrefsUpdateTimestampStart|
+  //   |kPrefsSystemUpdatedMarker|, |kPrefsUpdateTimestampStart|,
+  //   |kPrefsCurrentBytesDownloaded|
   void ClearMetricsPrefs();
 
   DaemonStateInterface* daemon_state_;
diff --git a/update_attempter_android_unittest.cc b/update_attempter_android_unittest.cc
index ac6cec2..94452df 100644
--- a/update_attempter_android_unittest.cc
+++ b/update_attempter_android_unittest.cc
@@ -28,6 +28,7 @@
 #include "update_engine/common/fake_hardware.h"
 #include "update_engine/common/fake_prefs.h"
 #include "update_engine/common/mock_action_processor.h"
+#include "update_engine/common/test_utils.h"
 #include "update_engine/common/utils.h"
 #include "update_engine/daemon_state_android.h"
 #include "update_engine/mock_metrics_reporter.h"
@@ -38,6 +39,7 @@
 using update_engine::UpdateStatus;
 
 namespace chromeos_update_engine {
+
 class UpdateAttempterAndroidTest : public ::testing::Test {
  protected:
   UpdateAttempterAndroidTest() = default;
@@ -150,4 +152,58 @@
   EXPECT_TRUE(prefs_.Exists(kPrefsSystemUpdatedMarker));
 }
 
+TEST_F(UpdateAttempterAndroidTest, ReportMetricsForBytesDownloaded) {
+  // Check both prefs are updated correctly.
+  update_attempter_android_.BytesReceived(20, 50, 200);
+  EXPECT_EQ(
+      20,
+      metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, &prefs_));
+  EXPECT_EQ(
+      20,
+      metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, &prefs_));
+
+  EXPECT_CALL(*metrics_reporter_,
+              ReportUpdateAttemptDownloadMetrics(50, _, _, _, _))
+      .Times(1);
+  EXPECT_CALL(*metrics_reporter_,
+              ReportUpdateAttemptDownloadMetrics(40, _, _, _, _))
+      .Times(1);
+
+  int64_t total_bytes[kNumDownloadSources] = {};
+  total_bytes[kDownloadSourceHttpsServer] = 90;
+  EXPECT_CALL(*metrics_reporter_,
+              ReportSuccessfulUpdateMetrics(
+                  _,
+                  _,
+                  _,
+                  _,
+                  test_utils::DownloadSourceMatcher(total_bytes),
+                  125,
+                  _,
+                  _,
+                  _))
+      .Times(1);
+
+  // The first update fails after receving 50 bytes in total.
+  update_attempter_android_.BytesReceived(30, 50, 200);
+  update_attempter_android_.ProcessingDone(nullptr, ErrorCode::kError);
+  EXPECT_EQ(
+      0,
+      metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, &prefs_));
+  EXPECT_EQ(
+      50,
+      metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, &prefs_));
+
+  // The second update succeeds after receiving 40 bytes, which leads to a
+  // overhead of 50 / 40 = 125%.
+  update_attempter_android_.BytesReceived(40, 40, 50);
+  update_attempter_android_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+  // Both prefs should be cleared.
+  EXPECT_EQ(
+      0,
+      metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, &prefs_));
+  EXPECT_EQ(
+      0, metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, &prefs_));
+}
+
 }  // namespace chromeos_update_engine