Merge "Add maximum timestamp to the payload."
diff --git a/Android.mk b/Android.mk
index 8da8ede..8807666 100644
--- a/Android.mk
+++ b/Android.mk
@@ -356,6 +356,7 @@
     libbrillo-binder \
     libcutils \
     libcurl \
+    libmetricslogger \
     libssl \
     libutils
 
@@ -389,6 +390,8 @@
     daemon_state_android.cc \
     hardware_android.cc \
     libcurl_http_fetcher.cc \
+    metrics_reporter_android.cc \
+    metrics_utils.cc \
     network_selector_android.cc \
     proxy_resolver.cc \
     update_attempter_android.cc \
@@ -463,6 +466,8 @@
 LOCAL_SRC_FILES := \
     boot_control_recovery.cc \
     hardware_android.cc \
+    metrics_reporter_android.cc \
+    metrics_utils.cc \
     network_selector_stub.cc \
     proxy_resolver.cc \
     sideload_main.cc \
@@ -947,6 +952,7 @@
     common_service_unittest.cc \
     fake_system_state.cc \
     image_properties_android_unittest.cc \
+    metrics_reporter_omaha_unittest.cc \
     metrics_utils_unittest.cc \
     omaha_request_action_unittest.cc \
     omaha_request_params_unittest.cc \
diff --git a/metrics_reporter_android.cc b/metrics_reporter_android.cc
new file mode 100644
index 0000000..ebfad0d
--- /dev/null
+++ b/metrics_reporter_android.cc
@@ -0,0 +1,53 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/metrics_reporter_android.h"
+
+#ifndef _UE_SIDELOAD
+#include <metricslogger/metrics_logger.h>
+#endif  // _UE_SIDELOAD
+
+namespace chromeos_update_engine {
+
+namespace metrics {
+
+#ifndef _UE_SIDELOAD
+const char kMetricsUpdateEngineErrorCode[] = "ota_update_engine_error_code";
+#endif
+}  // namespace metrics
+
+void MetricsReporterAndroid::ReportUpdateAttemptMetrics(
+    SystemState* /* system_state */,
+    int /* attempt_number */,
+    PayloadType /* payload_type */,
+    base::TimeDelta /* duration */,
+    base::TimeDelta /* duration_uptime */,
+    int64_t /* payload_size */,
+    int64_t /* payload_bytes_downloaded */,
+    int64_t /* payload_download_speed_bps */,
+    DownloadSource /* download_source */,
+    metrics::AttemptResult /* attempt_result */,
+    ErrorCode error_code,
+    metrics::DownloadErrorCode /* payload_download_error_code */,
+    metrics::ConnectionType /* connection_type */) {
+// No need to log histogram under sideload mode.
+#ifndef _UE_SIDELOAD
+  android::metricslogger::LogHistogram(metrics::kMetricsUpdateEngineErrorCode,
+                                       static_cast<int>(error_code));
+#endif
+}
+
+};  // namespace chromeos_update_engine
diff --git a/metrics_reporter_android.h b/metrics_reporter_android.h
new file mode 100644
index 0000000..9ee8bf5
--- /dev/null
+++ b/metrics_reporter_android.h
@@ -0,0 +1,87 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_METRICS_REPORTER_ANDROID_H_
+#define UPDATE_ENGINE_METRICS_REPORTER_ANDROID_H_
+
+#include "update_engine/common/error_code.h"
+#include "update_engine/metrics_constants.h"
+#include "update_engine/metrics_reporter_interface.h"
+
+namespace chromeos_update_engine {
+
+class MetricsReporterAndroid : public MetricsReporterInterface {
+ public:
+  MetricsReporterAndroid() = default;
+
+  ~MetricsReporterAndroid() override = default;
+
+  void Initialize() override {}
+
+  void ReportRollbackMetrics(metrics::RollbackResult result) override {}
+
+  void ReportDailyMetrics(base::TimeDelta os_age) override {}
+
+  void ReportUpdateCheckMetrics(
+      SystemState* system_state,
+      metrics::CheckResult result,
+      metrics::CheckReaction reaction,
+      metrics::DownloadErrorCode download_error_code) override {}
+
+  void ReportUpdateAttemptMetrics(
+      SystemState* system_state,
+      int attempt_number,
+      PayloadType payload_type,
+      base::TimeDelta duration,
+      base::TimeDelta duration_uptime,
+      int64_t payload_size,
+      int64_t payload_bytes_downloaded,
+      int64_t payload_download_speed_bps,
+      DownloadSource download_source,
+      metrics::AttemptResult attempt_result,
+      ErrorCode internal_error_code,
+      metrics::DownloadErrorCode payload_download_error_code,
+      metrics::ConnectionType connection_type) override;
+
+  void ReportAbnormallyTerminatedUpdateAttemptMetrics() override {}
+
+  void ReportSuccessfulUpdateMetrics(
+      int attempt_count,
+      int updates_abandoned_count,
+      PayloadType payload_type,
+      int64_t payload_size,
+      int64_t num_bytes_downloaded[kNumDownloadSources],
+      int download_overhead_percentage,
+      base::TimeDelta total_duration,
+      int reboot_count,
+      int url_switch_count) override {}
+
+  void ReportCertificateCheckMetrics(ServerToCheck server_to_check,
+                                     CertificateCheckResult result) override {}
+
+  void ReportFailedUpdateCount(int target_attempt) override {}
+
+  void ReportTimeToReboot(int time_to_reboot_minutes) override {}
+
+  void ReportInstallDateProvisioningSource(int source, int max) override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MetricsReporterAndroid);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_METRICS_REPORTER_ANDROID_H_
diff --git a/metrics_reporter_interface.h b/metrics_reporter_interface.h
index 458d98e..0c8a025 100644
--- a/metrics_reporter_interface.h
+++ b/metrics_reporter_interface.h
@@ -19,7 +19,6 @@
 
 #include <base/time/time.h>
 
-#include "update_engine/certificate_checker.h"
 #include "update_engine/common/constants.h"
 #include "update_engine/common/error_code.h"
 #include "update_engine/metrics_constants.h"
@@ -27,6 +26,9 @@
 
 namespace chromeos_update_engine {
 
+enum class ServerToCheck;
+enum class CertificateCheckResult;
+
 class MetricsReporterInterface {
  public:
   virtual ~MetricsReporterInterface() = default;
@@ -80,8 +82,8 @@
   //  |kMetricAttemptNumber|
   //  |kMetricAttemptPayloadType|
   //  |kMetricAttemptPayloadSizeMiB|
-  //  |kMetricAttemptDurationSeconds|
-  //  |kMetricAttemptDurationUptimeSeconds|
+  //  |kMetricAttemptDurationMinutes|
+  //  |kMetricAttemptDurationUptimeMinutes|
   //  |kMetricAttemptTimeSinceLastAttemptMinutes|
   //  |kMetricAttemptTimeSinceLastAttemptUptimeMinutes|
   //  |kMetricAttemptPayloadBytesDownloadedMiB|
diff --git a/metrics_reporter_omaha.cc b/metrics_reporter_omaha.cc
index fcbe7e4..3aaf1c6 100644
--- a/metrics_reporter_omaha.cc
+++ b/metrics_reporter_omaha.cc
@@ -16,6 +16,7 @@
 
 #include "update_engine/metrics_reporter_omaha.h"
 
+#include <memory>
 #include <string>
 
 #include <base/logging.h>
@@ -32,97 +33,103 @@
 
 namespace chromeos_update_engine {
 
+namespace metrics {
+
 // UpdateEngine.Daily.* metrics.
-constexpr char kMetricDailyOSAgeDays[] = "UpdateEngine.Daily.OSAgeDays";
+const char kMetricDailyOSAgeDays[] = "UpdateEngine.Daily.OSAgeDays";
 
 // UpdateEngine.Check.* metrics.
-constexpr char kMetricCheckDownloadErrorCode[] =
+const char kMetricCheckDownloadErrorCode[] =
     "UpdateEngine.Check.DownloadErrorCode";
-constexpr char kMetricCheckReaction[] = "UpdateEngine.Check.Reaction";
-constexpr char kMetricCheckResult[] = "UpdateEngine.Check.Result";
-constexpr char kMetricCheckTimeSinceLastCheckMinutes[] =
+const char kMetricCheckReaction[] = "UpdateEngine.Check.Reaction";
+const char kMetricCheckResult[] = "UpdateEngine.Check.Result";
+const char kMetricCheckTimeSinceLastCheckMinutes[] =
     "UpdateEngine.Check.TimeSinceLastCheckMinutes";
-constexpr char kMetricCheckTimeSinceLastCheckUptimeMinutes[] =
+const char kMetricCheckTimeSinceLastCheckUptimeMinutes[] =
     "UpdateEngine.Check.TimeSinceLastCheckUptimeMinutes";
 
 // UpdateEngine.Attempt.* metrics.
-constexpr char kMetricAttemptNumber[] = "UpdateEngine.Attempt.Number";
-constexpr char kMetricAttemptPayloadType[] = "UpdateEngine.Attempt.PayloadType";
-constexpr char kMetricAttemptPayloadSizeMiB[] =
+const char kMetricAttemptNumber[] = "UpdateEngine.Attempt.Number";
+const char kMetricAttemptPayloadType[] = "UpdateEngine.Attempt.PayloadType";
+const char kMetricAttemptPayloadSizeMiB[] =
     "UpdateEngine.Attempt.PayloadSizeMiB";
-constexpr char kMetricAttemptConnectionType[] =
+const char kMetricAttemptConnectionType[] =
     "UpdateEngine.Attempt.ConnectionType";
-constexpr char kMetricAttemptDurationMinutes[] =
+const char kMetricAttemptDurationMinutes[] =
     "UpdateEngine.Attempt.DurationMinutes";
-constexpr char kMetricAttemptDurationUptimeMinutes[] =
+const char kMetricAttemptDurationUptimeMinutes[] =
     "UpdateEngine.Attempt.DurationUptimeMinutes";
-constexpr char kMetricAttemptTimeSinceLastAttemptMinutes[] =
+const char kMetricAttemptTimeSinceLastAttemptMinutes[] =
     "UpdateEngine.Attempt.TimeSinceLastAttemptMinutes";
-constexpr char kMetricAttemptTimeSinceLastAttemptUptimeMinutes[] =
+const char kMetricAttemptTimeSinceLastAttemptUptimeMinutes[] =
     "UpdateEngine.Attempt.TimeSinceLastAttemptUptimeMinutes";
-constexpr char kMetricAttemptPayloadBytesDownloadedMiB[] =
+const char kMetricAttemptPayloadBytesDownloadedMiB[] =
     "UpdateEngine.Attempt.PayloadBytesDownloadedMiB";
-constexpr char kMetricAttemptPayloadDownloadSpeedKBps[] =
+const char kMetricAttemptPayloadDownloadSpeedKBps[] =
     "UpdateEngine.Attempt.PayloadDownloadSpeedKBps";
-constexpr char kMetricAttemptDownloadSource[] =
+const char kMetricAttemptDownloadSource[] =
     "UpdateEngine.Attempt.DownloadSource";
-constexpr char kMetricAttemptResult[] = "UpdateEngine.Attempt.Result";
-constexpr char kMetricAttemptInternalErrorCode[] =
+const char kMetricAttemptResult[] = "UpdateEngine.Attempt.Result";
+const char kMetricAttemptInternalErrorCode[] =
     "UpdateEngine.Attempt.InternalErrorCode";
-constexpr char kMetricAttemptDownloadErrorCode[] =
+const char kMetricAttemptDownloadErrorCode[] =
     "UpdateEngine.Attempt.DownloadErrorCode";
 
 // UpdateEngine.SuccessfulUpdate.* metrics.
-constexpr char kMetricSuccessfulUpdateAttemptCount[] =
+const char kMetricSuccessfulUpdateAttemptCount[] =
     "UpdateEngine.SuccessfulUpdate.AttemptCount";
-constexpr char kMetricSuccessfulUpdateBytesDownloadedMiB[] =
+const char kMetricSuccessfulUpdateBytesDownloadedMiB[] =
     "UpdateEngine.SuccessfulUpdate.BytesDownloadedMiB";
-constexpr char kMetricSuccessfulUpdateDownloadOverheadPercentage[] =
+const char kMetricSuccessfulUpdateDownloadOverheadPercentage[] =
     "UpdateEngine.SuccessfulUpdate.DownloadOverheadPercentage";
-constexpr char kMetricSuccessfulUpdateDownloadSourcesUsed[] =
+const char kMetricSuccessfulUpdateDownloadSourcesUsed[] =
     "UpdateEngine.SuccessfulUpdate.DownloadSourcesUsed";
-constexpr char kMetricSuccessfulUpdatePayloadType[] =
+const char kMetricSuccessfulUpdatePayloadType[] =
     "UpdateEngine.SuccessfulUpdate.PayloadType";
-constexpr char kMetricSuccessfulUpdatePayloadSizeMiB[] =
+const char kMetricSuccessfulUpdatePayloadSizeMiB[] =
     "UpdateEngine.SuccessfulUpdate.PayloadSizeMiB";
-constexpr char kMetricSuccessfulUpdateRebootCount[] =
+const char kMetricSuccessfulUpdateRebootCount[] =
     "UpdateEngine.SuccessfulUpdate.RebootCount";
-constexpr char kMetricSuccessfulUpdateTotalDurationMinutes[] =
+const char kMetricSuccessfulUpdateTotalDurationMinutes[] =
     "UpdateEngine.SuccessfulUpdate.TotalDurationMinutes";
-constexpr char kMetricSuccessfulUpdateUpdatesAbandonedCount[] =
+const char kMetricSuccessfulUpdateUpdatesAbandonedCount[] =
     "UpdateEngine.SuccessfulUpdate.UpdatesAbandonedCount";
-constexpr char kMetricSuccessfulUpdateUrlSwitchCount[] =
+const char kMetricSuccessfulUpdateUrlSwitchCount[] =
     "UpdateEngine.SuccessfulUpdate.UrlSwitchCount";
 
 // UpdateEngine.Rollback.* metric.
-constexpr char kMetricRollbackResult[] = "UpdateEngine.Rollback.Result";
+const char kMetricRollbackResult[] = "UpdateEngine.Rollback.Result";
 
 // UpdateEngine.CertificateCheck.* metrics.
-constexpr char kMetricCertificateCheckUpdateCheck[] =
+const char kMetricCertificateCheckUpdateCheck[] =
     "UpdateEngine.CertificateCheck.UpdateCheck";
-constexpr char kMetricCertificateCheckDownload[] =
+const char kMetricCertificateCheckDownload[] =
     "UpdateEngine.CertificateCheck.Download";
 
 // UpdateEngine.* metrics.
-constexpr char kMetricFailedUpdateCount[] = "UpdateEngine.FailedUpdateCount";
-constexpr char kMetricInstallDateProvisioningSource[] =
+const char kMetricFailedUpdateCount[] = "UpdateEngine.FailedUpdateCount";
+const char kMetricInstallDateProvisioningSource[] =
     "UpdateEngine.InstallDateProvisioningSource";
-constexpr char kMetricTimeToRebootMinutes[] =
-    "UpdateEngine.TimeToRebootMinutes";
+const char kMetricTimeToRebootMinutes[] = "UpdateEngine.TimeToRebootMinutes";
+
+}  // namespace metrics
+
+MetricsReporterOmaha::MetricsReporterOmaha()
+    : metrics_lib_(new MetricsLibrary()) {}
 
 void MetricsReporterOmaha::Initialize() {
-  metrics_lib_.Init();
+  metrics_lib_->Init();
 }
 
 void MetricsReporterOmaha::ReportDailyMetrics(base::TimeDelta os_age) {
-  string metric = kMetricDailyOSAgeDays;
+  string metric = metrics::kMetricDailyOSAgeDays;
   LOG(INFO) << "Uploading " << utils::FormatTimeDelta(os_age) << " for metric "
             << metric;
-  metrics_lib_.SendToUMA(metric,
-                         static_cast<int>(os_age.InDays()),
-                         0,       // min: 0 days
-                         6 * 30,  // max: 6 months (approx)
-                         50);     // num_buckets
+  metrics_lib_->SendToUMA(metric,
+                          static_cast<int>(os_age.InDays()),
+                          0,       // min: 0 days
+                          6 * 30,  // max: 6 months (approx)
+                          50);     // num_buckets
 }
 
 void MetricsReporterOmaha::ReportUpdateCheckMetrics(
@@ -135,24 +142,24 @@
   int max_value;
 
   if (result != metrics::CheckResult::kUnset) {
-    metric = kMetricCheckResult;
+    metric = metrics::kMetricCheckResult;
     value = static_cast<int>(result);
     max_value = static_cast<int>(metrics::CheckResult::kNumConstants) - 1;
     LOG(INFO) << "Sending " << value << " for metric " << metric << " (enum)";
-    metrics_lib_.SendEnumToUMA(metric, value, max_value);
+    metrics_lib_->SendEnumToUMA(metric, value, max_value);
   }
   if (reaction != metrics::CheckReaction::kUnset) {
-    metric = kMetricCheckReaction;
+    metric = metrics::kMetricCheckReaction;
     value = static_cast<int>(reaction);
     max_value = static_cast<int>(metrics::CheckReaction::kNumConstants) - 1;
     LOG(INFO) << "Sending " << value << " for metric " << metric << " (enum)";
-    metrics_lib_.SendEnumToUMA(metric, value, max_value);
+    metrics_lib_->SendEnumToUMA(metric, value, max_value);
   }
   if (download_error_code != metrics::DownloadErrorCode::kUnset) {
-    metric = kMetricCheckDownloadErrorCode;
+    metric = metrics::kMetricCheckDownloadErrorCode;
     value = static_cast<int>(download_error_code);
     LOG(INFO) << "Sending " << value << " for metric " << metric << " (sparse)";
-    metrics_lib_.SendSparseToUMA(metric, value);
+    metrics_lib_->SendSparseToUMA(metric, value);
   }
 
   base::TimeDelta time_since_last;
@@ -160,39 +167,39 @@
           system_state,
           kPrefsMetricsCheckLastReportingTime,
           &time_since_last)) {
-    metric = kMetricCheckTimeSinceLastCheckMinutes;
+    metric = metrics::kMetricCheckTimeSinceLastCheckMinutes;
     LOG(INFO) << "Sending " << utils::FormatTimeDelta(time_since_last)
               << " for metric " << metric;
-    metrics_lib_.SendToUMA(metric,
-                           time_since_last.InMinutes(),
-                           0,             // min: 0 min
-                           30 * 24 * 60,  // max: 30 days
-                           50);           // num_buckets
+    metrics_lib_->SendToUMA(metric,
+                            time_since_last.InMinutes(),
+                            0,             // min: 0 min
+                            30 * 24 * 60,  // max: 30 days
+                            50);           // num_buckets
   }
 
   base::TimeDelta uptime_since_last;
   static int64_t uptime_since_last_storage = 0;
   if (metrics_utils::MonotonicDurationHelper(
           system_state, &uptime_since_last_storage, &uptime_since_last)) {
-    metric = kMetricCheckTimeSinceLastCheckUptimeMinutes;
+    metric = metrics::kMetricCheckTimeSinceLastCheckUptimeMinutes;
     LOG(INFO) << "Sending " << utils::FormatTimeDelta(uptime_since_last)
               << " for metric " << metric;
-    metrics_lib_.SendToUMA(metric,
-                           uptime_since_last.InMinutes(),
-                           0,             // min: 0 min
-                           30 * 24 * 60,  // max: 30 days
-                           50);           // num_buckets
+    metrics_lib_->SendToUMA(metric,
+                            uptime_since_last.InMinutes(),
+                            0,             // min: 0 min
+                            30 * 24 * 60,  // max: 30 days
+                            50);           // num_buckets
   }
 }
 
 void MetricsReporterOmaha::ReportAbnormallyTerminatedUpdateAttemptMetrics() {
-  string metric = kMetricAttemptResult;
+  string metric = metrics::kMetricAttemptResult;
   metrics::AttemptResult attempt_result =
       metrics::AttemptResult::kAbnormalTermination;
 
   LOG(INFO) << "Uploading " << static_cast<int>(attempt_result)
             << " for metric " << metric;
-  metrics_lib_.SendEnumToUMA(
+  metrics_lib_->SendEnumToUMA(
       metric,
       static_cast<int>(attempt_result),
       static_cast<int>(metrics::AttemptResult::kNumConstants));
@@ -212,94 +219,94 @@
     ErrorCode internal_error_code,
     metrics::DownloadErrorCode payload_download_error_code,
     metrics::ConnectionType connection_type) {
-  string metric = kMetricAttemptNumber;
+  string metric = metrics::kMetricAttemptNumber;
   LOG(INFO) << "Uploading " << attempt_number << " for metric " << metric;
-  metrics_lib_.SendToUMA(metric,
-                         attempt_number,
-                         0,    // min: 0 attempts
-                         49,   // max: 49 attempts
-                         50);  // num_buckets
+  metrics_lib_->SendToUMA(metric,
+                          attempt_number,
+                          0,    // min: 0 attempts
+                          49,   // max: 49 attempts
+                          50);  // num_buckets
 
-  metric = kMetricAttemptPayloadType;
+  metric = metrics::kMetricAttemptPayloadType;
   LOG(INFO) << "Uploading " << utils::ToString(payload_type) << " for metric "
             << metric;
-  metrics_lib_.SendEnumToUMA(metric, payload_type, kNumPayloadTypes);
+  metrics_lib_->SendEnumToUMA(metric, payload_type, kNumPayloadTypes);
 
-  metric = kMetricAttemptDurationMinutes;
+  metric = metrics::kMetricAttemptDurationMinutes;
   LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration)
             << " for metric " << metric;
-  metrics_lib_.SendToUMA(metric,
-                         duration.InMinutes(),
-                         0,             // min: 0 min
-                         10 * 24 * 60,  // max: 10 days
-                         50);           // num_buckets
+  metrics_lib_->SendToUMA(metric,
+                          duration.InMinutes(),
+                          0,             // min: 0 min
+                          10 * 24 * 60,  // max: 10 days
+                          50);           // num_buckets
 
-  metric = kMetricAttemptDurationUptimeMinutes;
+  metric = metrics::kMetricAttemptDurationUptimeMinutes;
   LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration_uptime)
             << " for metric " << metric;
-  metrics_lib_.SendToUMA(metric,
-                         duration_uptime.InMinutes(),
-                         0,             // min: 0 min
-                         10 * 24 * 60,  // max: 10 days
-                         50);           // num_buckets
+  metrics_lib_->SendToUMA(metric,
+                          duration_uptime.InMinutes(),
+                          0,             // min: 0 min
+                          10 * 24 * 60,  // max: 10 days
+                          50);           // num_buckets
 
-  metric = kMetricAttemptPayloadSizeMiB;
+  metric = metrics::kMetricAttemptPayloadSizeMiB;
   int64_t payload_size_mib = payload_size / kNumBytesInOneMiB;
   LOG(INFO) << "Uploading " << payload_size_mib << " for metric " << metric;
-  metrics_lib_.SendToUMA(metric,
-                         payload_size_mib,
-                         0,     // min: 0 MiB
-                         1024,  // max: 1024 MiB = 1 GiB
-                         50);   // num_buckets
+  metrics_lib_->SendToUMA(metric,
+                          payload_size_mib,
+                          0,     // min: 0 MiB
+                          1024,  // max: 1024 MiB = 1 GiB
+                          50);   // num_buckets
 
-  metric = kMetricAttemptPayloadBytesDownloadedMiB;
+  metric = metrics::kMetricAttemptPayloadBytesDownloadedMiB;
   int64_t payload_bytes_downloaded_mib =
       payload_bytes_downloaded / kNumBytesInOneMiB;
   LOG(INFO) << "Uploading " << payload_bytes_downloaded_mib << " for metric "
             << metric;
-  metrics_lib_.SendToUMA(metric,
-                         payload_bytes_downloaded_mib,
-                         0,     // min: 0 MiB
-                         1024,  // max: 1024 MiB = 1 GiB
-                         50);   // num_buckets
+  metrics_lib_->SendToUMA(metric,
+                          payload_bytes_downloaded_mib,
+                          0,     // min: 0 MiB
+                          1024,  // max: 1024 MiB = 1 GiB
+                          50);   // num_buckets
 
-  metric = kMetricAttemptPayloadDownloadSpeedKBps;
+  metric = metrics::kMetricAttemptPayloadDownloadSpeedKBps;
   int64_t payload_download_speed_kbps = payload_download_speed_bps / 1000;
   LOG(INFO) << "Uploading " << payload_download_speed_kbps << " for metric "
             << metric;
-  metrics_lib_.SendToUMA(metric,
-                         payload_download_speed_kbps,
-                         0,          // min: 0 kB/s
-                         10 * 1000,  // max: 10000 kB/s = 10 MB/s
-                         50);        // num_buckets
+  metrics_lib_->SendToUMA(metric,
+                          payload_download_speed_kbps,
+                          0,          // min: 0 kB/s
+                          10 * 1000,  // max: 10000 kB/s = 10 MB/s
+                          50);        // num_buckets
 
-  metric = kMetricAttemptDownloadSource;
+  metric = metrics::kMetricAttemptDownloadSource;
   LOG(INFO) << "Uploading " << download_source << " for metric " << metric;
-  metrics_lib_.SendEnumToUMA(metric, download_source, kNumDownloadSources);
+  metrics_lib_->SendEnumToUMA(metric, download_source, kNumDownloadSources);
 
-  metric = kMetricAttemptResult;
+  metric = metrics::kMetricAttemptResult;
   LOG(INFO) << "Uploading " << static_cast<int>(attempt_result)
             << " for metric " << metric;
-  metrics_lib_.SendEnumToUMA(
+  metrics_lib_->SendEnumToUMA(
       metric,
       static_cast<int>(attempt_result),
       static_cast<int>(metrics::AttemptResult::kNumConstants));
 
   if (internal_error_code != ErrorCode::kSuccess) {
-    metric = kMetricAttemptInternalErrorCode;
+    metric = metrics::kMetricAttemptInternalErrorCode;
     LOG(INFO) << "Uploading " << internal_error_code << " for metric "
               << metric;
-    metrics_lib_.SendEnumToUMA(metric,
-                               static_cast<int>(internal_error_code),
-                               static_cast<int>(ErrorCode::kUmaReportedMax));
+    metrics_lib_->SendEnumToUMA(metric,
+                                static_cast<int>(internal_error_code),
+                                static_cast<int>(ErrorCode::kUmaReportedMax));
   }
 
   if (payload_download_error_code != metrics::DownloadErrorCode::kUnset) {
-    metric = kMetricAttemptDownloadErrorCode;
+    metric = metrics::kMetricAttemptDownloadErrorCode;
     LOG(INFO) << "Uploading " << static_cast<int>(payload_download_error_code)
               << " for metric " << metric << " (sparse)";
-    metrics_lib_.SendSparseToUMA(metric,
-                                 static_cast<int>(payload_download_error_code));
+    metrics_lib_->SendSparseToUMA(
+        metric, static_cast<int>(payload_download_error_code));
   }
 
   base::TimeDelta time_since_last;
@@ -307,34 +314,34 @@
           system_state,
           kPrefsMetricsAttemptLastReportingTime,
           &time_since_last)) {
-    metric = kMetricAttemptTimeSinceLastAttemptMinutes;
+    metric = metrics::kMetricAttemptTimeSinceLastAttemptMinutes;
     LOG(INFO) << "Sending " << utils::FormatTimeDelta(time_since_last)
               << " for metric " << metric;
-    metrics_lib_.SendToUMA(metric,
-                           time_since_last.InMinutes(),
-                           0,             // min: 0 min
-                           30 * 24 * 60,  // max: 30 days
-                           50);           // num_buckets
+    metrics_lib_->SendToUMA(metric,
+                            time_since_last.InMinutes(),
+                            0,             // min: 0 min
+                            30 * 24 * 60,  // max: 30 days
+                            50);           // num_buckets
   }
 
   static int64_t uptime_since_last_storage = 0;
   base::TimeDelta uptime_since_last;
   if (metrics_utils::MonotonicDurationHelper(
           system_state, &uptime_since_last_storage, &uptime_since_last)) {
-    metric = kMetricAttemptTimeSinceLastAttemptUptimeMinutes;
+    metric = metrics::kMetricAttemptTimeSinceLastAttemptUptimeMinutes;
     LOG(INFO) << "Sending " << utils::FormatTimeDelta(uptime_since_last)
               << " for metric " << metric;
-    metrics_lib_.SendToUMA(metric,
-                           uptime_since_last.InMinutes(),
-                           0,             // min: 0 min
-                           30 * 24 * 60,  // max: 30 days
-                           50);           // num_buckets
+    metrics_lib_->SendToUMA(metric,
+                            uptime_since_last.InMinutes(),
+                            0,             // min: 0 min
+                            30 * 24 * 60,  // max: 30 days
+                            50);           // num_buckets
   }
 
-  metric = kMetricAttemptConnectionType;
+  metric = metrics::kMetricAttemptConnectionType;
   LOG(INFO) << "Uploading " << static_cast<int>(connection_type)
             << " for metric " << metric;
-  metrics_lib_.SendEnumToUMA(
+  metrics_lib_->SendEnumToUMA(
       metric,
       static_cast<int>(connection_type),
       static_cast<int>(metrics::ConnectionType::kNumConstants));
@@ -350,14 +357,14 @@
     base::TimeDelta total_duration,
     int reboot_count,
     int url_switch_count) {
-  string metric = kMetricSuccessfulUpdatePayloadSizeMiB;
+  string metric = metrics::kMetricSuccessfulUpdatePayloadSizeMiB;
   int64_t mbs = payload_size / kNumBytesInOneMiB;
   LOG(INFO) << "Uploading " << mbs << " (MiBs) for metric " << metric;
-  metrics_lib_.SendToUMA(metric,
-                         mbs,
-                         0,     // min: 0 MiB
-                         1024,  // max: 1024 MiB = 1 GiB
-                         50);   // num_buckets
+  metrics_lib_->SendToUMA(metric,
+                          mbs,
+                          0,     // min: 0 MiB
+                          1024,  // max: 1024 MiB = 1 GiB
+                          50);   // num_buckets
 
   int64_t total_bytes = 0;
   int download_sources_used = 0;
@@ -370,7 +377,7 @@
     // update. Otherwise we're going to end up with a lot of zero-byte
     // events in the histogram.
 
-    metric = kMetricSuccessfulUpdateBytesDownloadedMiB;
+    metric = metrics::kMetricSuccessfulUpdateBytesDownloadedMiB;
     if (i < kNumDownloadSources) {
       metric += utils::ToString(source);
       mbs = num_bytes_downloaded[i] / kNumBytesInOneMiB;
@@ -383,88 +390,88 @@
 
     if (mbs > 0) {
       LOG(INFO) << "Uploading " << mbs << " (MiBs) for metric " << metric;
-      metrics_lib_.SendToUMA(metric,
-                             mbs,
-                             0,     // min: 0 MiB
-                             1024,  // max: 1024 MiB = 1 GiB
-                             50);   // num_buckets
+      metrics_lib_->SendToUMA(metric,
+                              mbs,
+                              0,     // min: 0 MiB
+                              1024,  // max: 1024 MiB = 1 GiB
+                              50);   // num_buckets
     }
   }
 
-  metric = kMetricSuccessfulUpdateDownloadSourcesUsed;
+  metric = metrics::kMetricSuccessfulUpdateDownloadSourcesUsed;
   LOG(INFO) << "Uploading 0x" << std::hex << download_sources_used
             << " (bit flags) for metric " << metric;
-  metrics_lib_.SendToUMA(metric,
-                         download_sources_used,
-                         0,                               // min
-                         (1 << kNumDownloadSources) - 1,  // max
-                         1 << kNumDownloadSources);       // num_buckets
+  metrics_lib_->SendToUMA(metric,
+                          download_sources_used,
+                          0,                               // min
+                          (1 << kNumDownloadSources) - 1,  // max
+                          1 << kNumDownloadSources);       // num_buckets
 
-  metric = kMetricSuccessfulUpdateDownloadOverheadPercentage;
+  metric = metrics::kMetricSuccessfulUpdateDownloadOverheadPercentage;
   LOG(INFO) << "Uploading " << download_overhead_percentage << "% for metric "
             << metric;
-  metrics_lib_.SendToUMA(metric,
-                         download_overhead_percentage,
-                         0,     // min: 0% overhead
-                         1000,  // max: 1000% overhead
-                         50);   // num_buckets
+  metrics_lib_->SendToUMA(metric,
+                          download_overhead_percentage,
+                          0,     // min: 0% overhead
+                          1000,  // max: 1000% overhead
+                          50);   // num_buckets
 
-  metric = kMetricSuccessfulUpdateUrlSwitchCount;
+  metric = metrics::kMetricSuccessfulUpdateUrlSwitchCount;
   LOG(INFO) << "Uploading " << url_switch_count << " (count) for metric "
             << metric;
-  metrics_lib_.SendToUMA(metric,
-                         url_switch_count,
-                         0,    // min: 0 URL switches
-                         49,   // max: 49 URL switches
-                         50);  // num_buckets
+  metrics_lib_->SendToUMA(metric,
+                          url_switch_count,
+                          0,    // min: 0 URL switches
+                          49,   // max: 49 URL switches
+                          50);  // num_buckets
 
-  metric = kMetricSuccessfulUpdateTotalDurationMinutes;
+  metric = metrics::kMetricSuccessfulUpdateTotalDurationMinutes;
   LOG(INFO) << "Uploading " << utils::FormatTimeDelta(total_duration)
             << " for metric " << metric;
-  metrics_lib_.SendToUMA(metric,
-                         static_cast<int>(total_duration.InMinutes()),
-                         0,              // min: 0 min
-                         365 * 24 * 60,  // max: 365 days ~= 1 year
-                         50);            // num_buckets
+  metrics_lib_->SendToUMA(metric,
+                          static_cast<int>(total_duration.InMinutes()),
+                          0,              // min: 0 min
+                          365 * 24 * 60,  // max: 365 days ~= 1 year
+                          50);            // num_buckets
 
-  metric = kMetricSuccessfulUpdateRebootCount;
+  metric = metrics::kMetricSuccessfulUpdateRebootCount;
   LOG(INFO) << "Uploading reboot count of " << reboot_count << " for metric "
             << metric;
-  metrics_lib_.SendToUMA(metric,
-                         reboot_count,
-                         0,    // min: 0 reboots
-                         49,   // max: 49 reboots
-                         50);  // num_buckets
+  metrics_lib_->SendToUMA(metric,
+                          reboot_count,
+                          0,    // min: 0 reboots
+                          49,   // max: 49 reboots
+                          50);  // num_buckets
 
-  metric = kMetricSuccessfulUpdatePayloadType;
-  metrics_lib_.SendEnumToUMA(metric, payload_type, kNumPayloadTypes);
+  metric = metrics::kMetricSuccessfulUpdatePayloadType;
+  metrics_lib_->SendEnumToUMA(metric, payload_type, kNumPayloadTypes);
   LOG(INFO) << "Uploading " << utils::ToString(payload_type) << " for metric "
             << metric;
 
-  metric = kMetricSuccessfulUpdateAttemptCount;
-  metrics_lib_.SendToUMA(metric,
-                         attempt_count,
-                         1,    // min: 1 attempt
-                         50,   // max: 50 attempts
-                         50);  // num_buckets
+  metric = metrics::kMetricSuccessfulUpdateAttemptCount;
+  metrics_lib_->SendToUMA(metric,
+                          attempt_count,
+                          1,    // min: 1 attempt
+                          50,   // max: 50 attempts
+                          50);  // num_buckets
   LOG(INFO) << "Uploading " << attempt_count << " for metric " << metric;
 
-  metric = kMetricSuccessfulUpdateUpdatesAbandonedCount;
+  metric = metrics::kMetricSuccessfulUpdateUpdatesAbandonedCount;
   LOG(INFO) << "Uploading " << updates_abandoned_count << " (count) for metric "
             << metric;
-  metrics_lib_.SendToUMA(metric,
-                         updates_abandoned_count,
-                         0,    // min: 0 counts
-                         49,   // max: 49 counts
-                         50);  // num_buckets
+  metrics_lib_->SendToUMA(metric,
+                          updates_abandoned_count,
+                          0,    // min: 0 counts
+                          49,   // max: 49 counts
+                          50);  // num_buckets
 }
 
 void MetricsReporterOmaha::ReportRollbackMetrics(
     metrics::RollbackResult result) {
-  string metric = kMetricRollbackResult;
+  string metric = metrics::kMetricRollbackResult;
   int value = static_cast<int>(result);
   LOG(INFO) << "Sending " << value << " for metric " << metric << " (enum)";
-  metrics_lib_.SendEnumToUMA(
+  metrics_lib_->SendEnumToUMA(
       metric, value, static_cast<int>(metrics::RollbackResult::kNumConstants));
 }
 
@@ -473,41 +480,41 @@
   string metric;
   switch (server_to_check) {
     case ServerToCheck::kUpdate:
-      metric = kMetricCertificateCheckUpdateCheck;
+      metric = metrics::kMetricCertificateCheckUpdateCheck;
       break;
     case ServerToCheck::kDownload:
-      metric = kMetricCertificateCheckDownload;
+      metric = metrics::kMetricCertificateCheckDownload;
       break;
     case ServerToCheck::kNone:
       return;
   }
   LOG(INFO) << "Uploading " << static_cast<int>(result) << " for metric "
             << metric;
-  metrics_lib_.SendEnumToUMA(
+  metrics_lib_->SendEnumToUMA(
       metric,
       static_cast<int>(result),
       static_cast<int>(CertificateCheckResult::kNumConstants));
 }
 
 void MetricsReporterOmaha::ReportFailedUpdateCount(int target_attempt) {
-  string metric = kMetricFailedUpdateCount;
-  metrics_lib_.SendToUMA(metric,
-                         target_attempt,
-                         1,   // min value
-                         50,  // max value
-                         kNumDefaultUmaBuckets);
+  string metric = metrics::kMetricFailedUpdateCount;
+  metrics_lib_->SendToUMA(metric,
+                          target_attempt,
+                          1,   // min value
+                          50,  // max value
+                          kNumDefaultUmaBuckets);
 
   LOG(INFO) << "Uploading " << target_attempt << " (count) for metric "
             << metric;
 }
 
 void MetricsReporterOmaha::ReportTimeToReboot(int time_to_reboot_minutes) {
-  string metric = kMetricTimeToRebootMinutes;
-  metrics_lib_.SendToUMA(metric,
-                         time_to_reboot_minutes,
-                         0,             // min: 0 minute
-                         30 * 24 * 60,  // max: 1 month (approx)
-                         kNumDefaultUmaBuckets);
+  string metric = metrics::kMetricTimeToRebootMinutes;
+  metrics_lib_->SendToUMA(metric,
+                          time_to_reboot_minutes,
+                          0,             // min: 0 minute
+                          30 * 24 * 60,  // max: 1 month (approx)
+                          kNumDefaultUmaBuckets);
 
   LOG(INFO) << "Uploading " << time_to_reboot_minutes << " for metric "
             << metric;
@@ -515,9 +522,9 @@
 
 void MetricsReporterOmaha::ReportInstallDateProvisioningSource(int source,
                                                                int max) {
-  metrics_lib_.SendEnumToUMA(kMetricInstallDateProvisioningSource,
-                             source,  // Sample.
-                             max);
+  metrics_lib_->SendEnumToUMA(metrics::kMetricInstallDateProvisioningSource,
+                              source,  // Sample.
+                              max);
 }
 
 }  // namespace chromeos_update_engine
diff --git a/metrics_reporter_omaha.h b/metrics_reporter_omaha.h
index e974a26..b209b13 100644
--- a/metrics_reporter_omaha.h
+++ b/metrics_reporter_omaha.h
@@ -17,6 +17,8 @@
 #ifndef UPDATE_ENGINE_METRICS_REPORTER_OMAHA_H_
 #define UPDATE_ENGINE_METRICS_REPORTER_OMAHA_H_
 
+#include <memory>
+
 #include <base/time/time.h>
 #include <metrics/metrics_library.h>
 
@@ -31,9 +33,63 @@
 
 class SystemState;
 
+namespace metrics {
+
+// UpdateEngine.Daily.* metrics.
+extern const char kMetricDailyOSAgeDays[];
+
+// UpdateEngine.Check.* metrics.
+extern const char kMetricCheckDownloadErrorCode[];
+extern const char kMetricCheckReaction[];
+extern const char kMetricCheckResult[];
+extern const char kMetricCheckTimeSinceLastCheckMinutes[];
+extern const char kMetricCheckTimeSinceLastCheckUptimeMinutes[];
+
+// UpdateEngine.Attempt.* metrics.
+extern const char kMetricAttemptNumber[];
+extern const char kMetricAttemptPayloadType[];
+extern const char kMetricAttemptPayloadSizeMiB[];
+extern const char kMetricAttemptConnectionType[];
+extern const char kMetricAttemptDurationMinutes[];
+extern const char kMetricAttemptDurationUptimeMinutes[];
+extern const char kMetricAttemptTimeSinceLastAttemptMinutes[];
+extern const char kMetricAttemptTimeSinceLastAttemptUptimeMinutes[];
+extern const char kMetricAttemptPayloadBytesDownloadedMiB[];
+extern const char kMetricAttemptPayloadDownloadSpeedKBps[];
+extern const char kMetricAttemptDownloadSource[];
+extern const char kMetricAttemptResult[];
+extern const char kMetricAttemptInternalErrorCode[];
+extern const char kMetricAttemptDownloadErrorCode[];
+
+// UpdateEngine.SuccessfulUpdate.* metrics.
+extern const char kMetricSuccessfulUpdateAttemptCount[];
+extern const char kMetricSuccessfulUpdateBytesDownloadedMiB[];
+extern const char kMetricSuccessfulUpdateDownloadOverheadPercentage[];
+extern const char kMetricSuccessfulUpdateDownloadSourcesUsed[];
+extern const char kMetricSuccessfulUpdatePayloadType[];
+extern const char kMetricSuccessfulUpdatePayloadSizeMiB[];
+extern const char kMetricSuccessfulUpdateRebootCount[];
+extern const char kMetricSuccessfulUpdateTotalDurationMinutes[];
+extern const char kMetricSuccessfulUpdateUpdatesAbandonedCount[];
+extern const char kMetricSuccessfulUpdateUrlSwitchCount[];
+
+// UpdateEngine.Rollback.* metric.
+extern const char kMetricRollbackResult[];
+
+// UpdateEngine.CertificateCheck.* metrics.
+extern const char kMetricCertificateCheckUpdateCheck[];
+extern const char kMetricCertificateCheckDownload[];
+
+// UpdateEngine.* metrics.
+extern const char kMetricFailedUpdateCount[];
+extern const char kMetricInstallDateProvisioningSource[];
+extern const char kMetricTimeToRebootMinutes[];
+
+}  // namespace metrics
+
 class MetricsReporterOmaha : public MetricsReporterInterface {
  public:
-  MetricsReporterOmaha() = default;
+  MetricsReporterOmaha();
 
   ~MetricsReporterOmaha() override = default;
 
@@ -87,7 +143,9 @@
   void ReportInstallDateProvisioningSource(int source, int max) override;
 
  private:
-  MetricsLibrary metrics_lib_;
+  friend class MetricsReporterOmahaTest;
+
+  std::unique_ptr<MetricsLibraryInterface> metrics_lib_;
 
   DISALLOW_COPY_AND_ASSIGN(MetricsReporterOmaha);
 };  // class metrics
diff --git a/metrics_reporter_omaha_unittest.cc b/metrics_reporter_omaha_unittest.cc
new file mode 100644
index 0000000..7a4102e
--- /dev/null
+++ b/metrics_reporter_omaha_unittest.cc
@@ -0,0 +1,395 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/metrics_reporter_omaha.h"
+
+#include <memory>
+#include <string>
+
+#include <base/time/time.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <metrics/metrics_library_mock.h>
+
+#include "update_engine/common/fake_clock.h"
+#include "update_engine/common/fake_prefs.h"
+#include "update_engine/fake_system_state.h"
+
+using base::TimeDelta;
+using testing::AnyNumber;
+using testing::_;
+
+namespace chromeos_update_engine {
+class MetricsReporterOmahaTest : public ::testing::Test {
+ protected:
+  MetricsReporterOmahaTest() = default;
+
+  // Reset the metrics_lib_ to a mock library.
+  void SetUp() override {
+    mock_metrics_lib_ = new testing::NiceMock<MetricsLibraryMock>();
+    reporter_.metrics_lib_.reset(mock_metrics_lib_);
+  }
+
+  testing::NiceMock<MetricsLibraryMock>* mock_metrics_lib_;
+  MetricsReporterOmaha reporter_;
+};
+
+TEST_F(MetricsReporterOmahaTest, ReportDailyMetrics) {
+  TimeDelta age = TimeDelta::FromDays(10);
+  EXPECT_CALL(*mock_metrics_lib_,
+              SendToUMA(metrics::kMetricDailyOSAgeDays, _, _, _, _))
+      .Times(1);
+
+  reporter_.ReportDailyMetrics(age);
+}
+
+TEST_F(MetricsReporterOmahaTest, ReportUpdateCheckMetrics) {
+  FakeSystemState fake_system_state;
+  FakeClock fake_clock;
+  FakePrefs fake_prefs;
+
+  // We need to execute the report twice to test the time since last report.
+  fake_system_state.set_clock(&fake_clock);
+  fake_system_state.set_prefs(&fake_prefs);
+  fake_clock.SetWallclockTime(base::Time::FromInternalValue(1000000));
+  fake_clock.SetMonotonicTime(base::Time::FromInternalValue(1000000));
+
+  metrics::CheckResult result = metrics::CheckResult::kUpdateAvailable;
+  metrics::CheckReaction reaction = metrics::CheckReaction::kIgnored;
+  metrics::DownloadErrorCode error_code =
+      metrics::DownloadErrorCode::kHttpStatus200;
+
+  EXPECT_CALL(
+      *mock_metrics_lib_,
+      SendEnumToUMA(metrics::kMetricCheckResult, static_cast<int>(result), _))
+      .Times(2);
+  EXPECT_CALL(*mock_metrics_lib_,
+              SendEnumToUMA(
+                  metrics::kMetricCheckReaction, static_cast<int>(reaction), _))
+      .Times(2);
+  EXPECT_CALL(*mock_metrics_lib_,
+              SendSparseToUMA(metrics::kMetricCheckDownloadErrorCode,
+                              static_cast<int>(error_code)))
+      .Times(2);
+
+  EXPECT_CALL(
+      *mock_metrics_lib_,
+      SendToUMA(metrics::kMetricCheckTimeSinceLastCheckMinutes, 1, _, _, _))
+      .Times(1);
+  EXPECT_CALL(
+      *mock_metrics_lib_,
+      SendToUMA(
+          metrics::kMetricCheckTimeSinceLastCheckUptimeMinutes, 1, _, _, _))
+      .Times(1);
+
+  reporter_.ReportUpdateCheckMetrics(
+      &fake_system_state, result, reaction, error_code);
+
+  // Advance the clock by 1 minute and report the same metrics again.
+  fake_clock.SetWallclockTime(base::Time::FromInternalValue(61000000));
+  fake_clock.SetMonotonicTime(base::Time::FromInternalValue(61000000));
+  reporter_.ReportUpdateCheckMetrics(
+      &fake_system_state, result, reaction, error_code);
+}
+
+TEST_F(MetricsReporterOmahaTest,
+       ReportAbnormallyTerminatedUpdateAttemptMetrics) {
+  EXPECT_CALL(*mock_metrics_lib_,
+              SendEnumToUMA(metrics::kMetricAttemptResult,
+                            static_cast<int>(
+                                metrics::AttemptResult::kAbnormalTermination),
+                            _))
+      .Times(1);
+
+  reporter_.ReportAbnormallyTerminatedUpdateAttemptMetrics();
+}
+
+TEST_F(MetricsReporterOmahaTest, ReportUpdateAttemptMetrics) {
+  FakeSystemState fake_system_state;
+  FakeClock fake_clock;
+  FakePrefs fake_prefs;
+
+  fake_system_state.set_clock(&fake_clock);
+  fake_system_state.set_prefs(&fake_prefs);
+  fake_clock.SetWallclockTime(base::Time::FromInternalValue(1000000));
+  fake_clock.SetMonotonicTime(base::Time::FromInternalValue(1000000));
+
+  int attempt_number = 1;
+  PayloadType payload_type = kPayloadTypeFull;
+  TimeDelta duration = TimeDelta::FromMinutes(1000);
+  TimeDelta duration_uptime = TimeDelta::FromMinutes(1000);
+
+  int64_t payload_size = 100 * kNumBytesInOneMiB;
+  int64_t payload_bytes_downloaded = 200 * kNumBytesInOneMiB;
+  int64_t payload_download_speed_bps = 100 * 1000;
+  DownloadSource download_source = kDownloadSourceHttpServer;
+  metrics::AttemptResult attempt_result =
+      metrics::AttemptResult::kInternalError;
+  ErrorCode internal_error_code = ErrorCode::kDownloadInvalidMetadataSignature;
+  metrics::DownloadErrorCode payload_download_error_code =
+      metrics::DownloadErrorCode::kDownloadError;
+  metrics::ConnectionType connection_type = metrics::ConnectionType::kCellular;
+
+  EXPECT_CALL(*mock_metrics_lib_,
+              SendToUMA(metrics::kMetricAttemptNumber, attempt_number, _, _, _))
+      .Times(2);
+  EXPECT_CALL(*mock_metrics_lib_,
+              SendEnumToUMA(metrics::kMetricAttemptPayloadType,
+                            static_cast<int>(payload_type),
+                            _))
+      .Times(2);
+  EXPECT_CALL(*mock_metrics_lib_,
+              SendToUMA(metrics::kMetricAttemptDurationMinutes,
+                        duration.InMinutes(),
+                        _,
+                        _,
+                        _))
+      .Times(2);
+  EXPECT_CALL(*mock_metrics_lib_,
+              SendToUMA(metrics::kMetricAttemptDurationUptimeMinutes,
+                        duration_uptime.InMinutes(),
+                        _,
+                        _,
+                        _))
+      .Times(2);
+
+  // Check the report of payload download metrics.
+  EXPECT_CALL(*mock_metrics_lib_,
+              SendToUMA(metrics::kMetricAttemptPayloadSizeMiB, 100, _, _, _))
+      .Times(2);
+  EXPECT_CALL(
+      *mock_metrics_lib_,
+      SendToUMA(metrics::kMetricAttemptPayloadBytesDownloadedMiB, 200, _, _, _))
+      .Times(2);
+  EXPECT_CALL(
+      *mock_metrics_lib_,
+      SendToUMA(metrics::kMetricAttemptPayloadDownloadSpeedKBps, 100, _, _, _))
+      .Times(2);
+  EXPECT_CALL(*mock_metrics_lib_,
+              SendEnumToUMA(metrics::kMetricAttemptDownloadSource,
+                            static_cast<int>(download_source),
+                            _))
+      .Times(2);
+
+  // Check the report of attempt result.
+  EXPECT_CALL(
+      *mock_metrics_lib_,
+      SendEnumToUMA(
+          metrics::kMetricAttemptResult, static_cast<int>(attempt_result), _))
+      .Times(2);
+  EXPECT_CALL(*mock_metrics_lib_,
+              SendEnumToUMA(metrics::kMetricAttemptInternalErrorCode,
+                            static_cast<int>(internal_error_code),
+                            _))
+      .Times(2);
+  EXPECT_CALL(*mock_metrics_lib_,
+              SendSparseToUMA(metrics::kMetricAttemptDownloadErrorCode,
+                              static_cast<int>(payload_download_error_code)))
+      .Times(2);
+
+  // Check the duration between two reports.
+  EXPECT_CALL(
+      *mock_metrics_lib_,
+      SendToUMA(metrics::kMetricAttemptTimeSinceLastAttemptMinutes, 1, _, _, _))
+      .Times(1);
+  EXPECT_CALL(
+      *mock_metrics_lib_,
+      SendToUMA(
+          metrics::kMetricAttemptTimeSinceLastAttemptUptimeMinutes, 1, _, _, _))
+      .Times(1);
+
+  EXPECT_CALL(*mock_metrics_lib_,
+              SendEnumToUMA(metrics::kMetricAttemptConnectionType,
+                            static_cast<int>(connection_type),
+                            _))
+      .Times(2);
+
+  reporter_.ReportUpdateAttemptMetrics(&fake_system_state,
+                                       attempt_number,
+                                       payload_type,
+                                       duration,
+                                       duration_uptime,
+                                       payload_size,
+                                       payload_bytes_downloaded,
+                                       payload_download_speed_bps,
+                                       download_source,
+                                       attempt_result,
+                                       internal_error_code,
+                                       payload_download_error_code,
+                                       connection_type);
+
+  // Advance the clock by 1 minute and report the same metrics again.
+  fake_clock.SetWallclockTime(base::Time::FromInternalValue(61000000));
+  fake_clock.SetMonotonicTime(base::Time::FromInternalValue(61000000));
+  reporter_.ReportUpdateAttemptMetrics(&fake_system_state,
+                                       attempt_number,
+                                       payload_type,
+                                       duration,
+                                       duration_uptime,
+                                       payload_size,
+                                       payload_bytes_downloaded,
+                                       payload_download_speed_bps,
+                                       download_source,
+                                       attempt_result,
+                                       internal_error_code,
+                                       payload_download_error_code,
+                                       connection_type);
+}
+
+TEST_F(MetricsReporterOmahaTest, ReportSuccessfulUpdateMetrics) {
+  int attempt_count = 3;
+  int updates_abandoned_count = 2;
+  PayloadType payload_type = kPayloadTypeDelta;
+  int64_t payload_size = 200 * kNumBytesInOneMiB;
+  int64_t num_bytes_downloaded[kNumDownloadSources] = {};
+  // 200MiB payload downloaded from HttpsServer.
+  num_bytes_downloaded[0] = 200 * kNumBytesInOneMiB;
+  int download_overhead_percentage = 20;
+  TimeDelta total_duration = TimeDelta::FromMinutes(30);
+  int reboot_count = 2;
+  int url_switch_count = 2;
+
+  EXPECT_CALL(
+      *mock_metrics_lib_,
+      SendToUMA(metrics::kMetricSuccessfulUpdatePayloadSizeMiB, 200, _, _, _))
+      .Times(1);
+
+  // Check the report to both BytesDownloadedMiBHttpsServer and
+  // BytesDownloadedMiB
+  std::string DownloadedMiBMetric =
+      metrics::kMetricSuccessfulUpdateBytesDownloadedMiB;
+  DownloadedMiBMetric += "HttpsServer";
+  EXPECT_CALL(*mock_metrics_lib_, SendToUMA(DownloadedMiBMetric, 200, _, _, _))
+      .Times(1);
+  EXPECT_CALL(
+      *mock_metrics_lib_,
+      SendToUMA(
+          metrics::kMetricSuccessfulUpdateBytesDownloadedMiB, 200, _, _, _))
+      .Times(1);
+
+  EXPECT_CALL(
+      *mock_metrics_lib_,
+      SendToUMA(
+          metrics::kMetricSuccessfulUpdateDownloadSourcesUsed, 1, _, _, _))
+      .Times(1);
+  EXPECT_CALL(
+      *mock_metrics_lib_,
+      SendToUMA(metrics::kMetricSuccessfulUpdateDownloadOverheadPercentage,
+                20,
+                _,
+                _,
+                _));
+
+  EXPECT_CALL(*mock_metrics_lib_,
+              SendToUMA(metrics::kMetricSuccessfulUpdateUrlSwitchCount,
+                        url_switch_count,
+                        _,
+                        _,
+                        _))
+      .Times(1);
+  EXPECT_CALL(
+      *mock_metrics_lib_,
+      SendToUMA(
+          metrics::kMetricSuccessfulUpdateTotalDurationMinutes, 30, _, _, _))
+      .Times(1);
+  EXPECT_CALL(
+      *mock_metrics_lib_,
+      SendToUMA(
+          metrics::kMetricSuccessfulUpdateRebootCount, reboot_count, _, _, _))
+      .Times(1);
+  EXPECT_CALL(*mock_metrics_lib_,
+              SendEnumToUMA(
+                  metrics::kMetricSuccessfulUpdatePayloadType, payload_type, _))
+      .Times(1);
+  EXPECT_CALL(
+      *mock_metrics_lib_,
+      SendToUMA(
+          metrics::kMetricSuccessfulUpdateAttemptCount, attempt_count, _, _, _))
+      .Times(1);
+  EXPECT_CALL(*mock_metrics_lib_,
+              SendToUMA(metrics::kMetricSuccessfulUpdateUpdatesAbandonedCount,
+                        updates_abandoned_count,
+                        _,
+                        _,
+                        _))
+      .Times(1);
+
+  reporter_.ReportSuccessfulUpdateMetrics(attempt_count,
+                                          updates_abandoned_count,
+                                          payload_type,
+                                          payload_size,
+                                          num_bytes_downloaded,
+                                          download_overhead_percentage,
+                                          total_duration,
+                                          reboot_count,
+                                          url_switch_count);
+}
+
+TEST_F(MetricsReporterOmahaTest, ReportRollbackMetrics) {
+  metrics::RollbackResult result = metrics::RollbackResult::kSuccess;
+  EXPECT_CALL(*mock_metrics_lib_,
+              SendEnumToUMA(
+                  metrics::kMetricRollbackResult, static_cast<int>(result), _))
+      .Times(1);
+
+  reporter_.ReportRollbackMetrics(result);
+}
+
+TEST_F(MetricsReporterOmahaTest, ReportCertificateCheckMetrics) {
+  ServerToCheck server_to_check = ServerToCheck::kUpdate;
+  CertificateCheckResult result = CertificateCheckResult::kValid;
+  EXPECT_CALL(*mock_metrics_lib_,
+              SendEnumToUMA(metrics::kMetricCertificateCheckUpdateCheck,
+                            static_cast<int>(result),
+                            _))
+      .Times(1);
+
+  reporter_.ReportCertificateCheckMetrics(server_to_check, result);
+}
+
+TEST_F(MetricsReporterOmahaTest, ReportFailedUpdateCount) {
+  int target_attempt = 3;
+  EXPECT_CALL(
+      *mock_metrics_lib_,
+      SendToUMA(metrics::kMetricFailedUpdateCount, target_attempt, _, _, _))
+      .Times(1);
+
+  reporter_.ReportFailedUpdateCount(target_attempt);
+}
+
+TEST_F(MetricsReporterOmahaTest, ReportTimeToReboot) {
+  int time_to_reboot_minutes = 1000;
+  EXPECT_CALL(
+      *mock_metrics_lib_,
+      SendToUMA(
+          metrics::kMetricTimeToRebootMinutes, time_to_reboot_minutes, _, _, _))
+      .Times(1);
+
+  reporter_.ReportTimeToReboot(time_to_reboot_minutes);
+}
+
+TEST_F(MetricsReporterOmahaTest, ReportInstallDateProvisioningSource) {
+  int source = 2;
+  int max = 5;
+  EXPECT_CALL(
+      *mock_metrics_lib_,
+      SendEnumToUMA(metrics::kMetricInstallDateProvisioningSource, source, max))
+      .Times(1);
+
+  reporter_.ReportInstallDateProvisioningSource(source, max);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/pylintrc b/pylintrc
index f264479..80a7605 100644
--- a/pylintrc
+++ b/pylintrc
@@ -149,7 +149,7 @@
 # (useful for modules/projects where namespaces are manipulated during runtime
 # and thus existing member attributes cannot be deduced by static analysis. It
 # supports qualified module names, as well as Unix pattern matching.
-ignored-modules=
+ignored-modules=update_payload.update_metadata_pb2
 
 # List of classes names for which member attributes should not be checked
 # (useful for classes with attributes dynamically set). This supports can work
diff --git a/scripts/update_device.py b/scripts/update_device.py
index a5be0a5..1a0daf8 100755
--- a/scripts/update_device.py
+++ b/scripts/update_device.py
@@ -19,12 +19,14 @@
 
 import argparse
 import BaseHTTPServer
+import hashlib
 import logging
 import os
 import socket
 import subprocess
 import sys
 import threading
+import xml.etree.ElementTree
 import zipfile
 
 import update_payload.payload
@@ -97,6 +99,7 @@
 
   Attributes:
     serving_payload: path to the only payload file we are serving.
+    serving_range: the start offset and size tuple of the payload.
   """
 
   @staticmethod
@@ -147,12 +150,12 @@
     else:
       self.send_response(200)
 
-    stat = os.fstat(f.fileno())
+    serving_start, serving_size = self.serving_range
     start_range, end_range = self._parse_range(self.headers.get('range'),
-                                               stat.st_size)
+                                               serving_size)
     logging.info('Serving request for %s from %s [%d, %d) length: %d',
-                 self.path, self.serving_payload, start_range, end_range,
-                 end_range - start_range)
+                 self.path, self.serving_payload, serving_start + start_range,
+                 serving_start + end_range, end_range - start_range)
 
     self.send_header('Accept-Ranges', 'bytes')
     self.send_header('Content-Range',
@@ -160,11 +163,12 @@
                      '/' + str(end_range - start_range))
     self.send_header('Content-Length', end_range - start_range)
 
+    stat = os.fstat(f.fileno())
     self.send_header('Last-Modified', self.date_time_string(stat.st_mtime))
     self.send_header('Content-type', 'application/octet-stream')
     self.end_headers()
 
-    f.seek(start_range)
+    f.seek(serving_start + start_range)
     CopyFileObjLength(f, self.wfile, copy_length=end_range - start_range)
 
 
@@ -184,49 +188,74 @@
       self.send_error(404, 'File not found')
       return
 
+    content_length = int(self.headers.getheader('Content-Length'))
+    request_xml = self.rfile.read(content_length)
+    xml_root = xml.etree.ElementTree.fromstring(request_xml)
+    appid = None
+    for app in xml_root.iter('app'):
+      if 'appid' in app.attrib:
+        appid = app.attrib['appid']
+        break
+    if not appid:
+      self.send_error(400, 'No appid in Omaha request')
+      return
+
     self.send_response(200)
     self.send_header("Content-type", "text/xml")
     self.end_headers()
 
-    stat = os.fstat(f.fileno())
-    sha256sum = subprocess.check_output(['sha256sum', self.serving_payload])
-    payload_hash = sha256sum.split()[0]
-    payload = update_payload.Payload(f)
+    serving_start, serving_size = self.serving_range
+    sha256 = hashlib.sha256()
+    f.seek(serving_start)
+    bytes_to_hash = serving_size
+    while bytes_to_hash:
+      buf = f.read(min(bytes_to_hash, 1024 * 1024))
+      if not buf:
+        self.send_error(500, 'Payload too small')
+        return
+      sha256.update(buf)
+      bytes_to_hash -= len(buf)
+
+    payload = update_payload.Payload(f, payload_file_offset=serving_start)
     payload.Init()
 
-    xml = '''
+    response_xml = '''
         <?xml version="1.0" encoding="UTF-8"?>
         <response protocol="3.0">
-          <app appid="appid">
+          <app appid="{appid}">
             <updatecheck status="ok">
               <urls>
-                <url codebase="http://127.0.0.1:%d/"/>
+                <url codebase="http://127.0.0.1:{port}/"/>
               </urls>
               <manifest version="0.0.0.1">
                 <actions>
                   <action event="install" run="payload"/>
-                  <action event="postinstall" MetadataSize="%d"/>
+                  <action event="postinstall" MetadataSize="{metadata_size}"/>
                 </actions>
                 <packages>
-                  <package hash_sha256="%s" name="payload" size="%d"/>
+                  <package hash_sha256="{payload_hash}" name="payload" size="{payload_size}"/>
                 </packages>
               </manifest>
             </updatecheck>
           </app>
         </response>
-    ''' % (DEVICE_PORT, payload.metadata_size, payload_hash, stat.st_size)
-    self.wfile.write(xml.strip())
+    '''.format(appid=appid, port=DEVICE_PORT,
+               metadata_size=payload.metadata_size,
+               payload_hash=sha256.hexdigest(),
+               payload_size=serving_size)
+    self.wfile.write(response_xml.strip())
     return
 
 
 class ServerThread(threading.Thread):
   """A thread for serving HTTP requests."""
 
-  def __init__(self, ota_filename):
+  def __init__(self, ota_filename, serving_range):
     threading.Thread.__init__(self)
-    # serving_payload is a class attribute and the UpdateHandler class is
-    # instantiated with every request.
+    # serving_payload and serving_range are class attributes and the
+    # UpdateHandler class is instantiated with every request.
     UpdateHandler.serving_payload = ota_filename
+    UpdateHandler.serving_range = serving_range
     self._httpd = BaseHTTPServer.HTTPServer(('127.0.0.1', 0), UpdateHandler)
     self.port = self._httpd.server_port
 
@@ -241,8 +270,8 @@
     self._httpd.socket.close()
 
 
-def StartServer(ota_filename):
-  t = ServerThread(ota_filename)
+def StartServer(ota_filename, serving_range):
+  t = ServerThread(ota_filename, serving_range)
   t.start()
   return t
 
@@ -318,8 +347,9 @@
 
 def main():
   parser = argparse.ArgumentParser(description='Android A/B OTA helper.')
-  parser.add_argument('otafile', metavar='ZIP', type=str,
-                      help='the OTA package file (a .zip file).')
+  parser.add_argument('otafile', metavar='PAYLOAD', type=str,
+                      help='the OTA package file (a .zip file) or raw payload \
+                      if device uses Omaha.')
   parser.add_argument('--file', action='store_true',
                       help='Push the file to the device before updating.')
   parser.add_argument('--no-push', action='store_true',
@@ -359,7 +389,12 @@
     # Update via sending the payload over the network with an "adb reverse"
     # command.
     payload_url = 'http://127.0.0.1:%d/payload' % DEVICE_PORT
-    server_thread = StartServer(args.otafile)
+    if use_omaha and zipfile.is_zipfile(args.otafile):
+      ota = AndroidOTAPackage(args.otafile)
+      serving_range = (ota.offset, ota.size)
+    else:
+      serving_range = (0, os.stat(args.otafile).st_size)
+    server_thread = StartServer(args.otafile, serving_range)
     cmds.append(
         ['reverse', 'tcp:%d' % DEVICE_PORT, 'tcp:%d' % server_thread.port])
     finalize_cmds.append(['reverse', '--remove', 'tcp:%d' % DEVICE_PORT])
diff --git a/scripts/update_payload/payload.py b/scripts/update_payload/payload.py
index f76c0de..184f805 100644
--- a/scripts/update_payload/payload.py
+++ b/scripts/update_payload/payload.py
@@ -9,12 +9,12 @@
 import hashlib
 import struct
 
-import applier
-import block_tracer
-import checker
-import common
-from error import PayloadError
-import update_metadata_pb2
+from update_payload import applier
+from update_payload import block_tracer
+from update_payload import checker
+from update_payload import common
+from update_payload.error import PayloadError
+from update_payload import update_metadata_pb2
 
 
 #
@@ -101,13 +101,15 @@
             hasher=hasher)
 
 
-  def __init__(self, payload_file):
+  def __init__(self, payload_file, payload_file_offset=0):
     """Initialize the payload object.
 
     Args:
       payload_file: update payload file object open for reading
+      payload_file_offset: the offset of the actual payload
     """
     self.payload_file = payload_file
+    self.payload_file_offset = payload_file_offset
     self.manifest_hasher = None
     self.is_init = False
     self.header = None
@@ -159,7 +161,8 @@
 
     return common.Read(
         self.payload_file, self.header.metadata_signature_len,
-        offset=self.header.size + self.header.manifest_len)
+        offset=self.payload_file_offset + self.header.size +
+        self.header.manifest_len)
 
   def ReadDataBlob(self, offset, length):
     """Reads and returns a single data blob from the update payload.
@@ -175,7 +178,8 @@
       PayloadError if a read error occurred.
     """
     return common.Read(self.payload_file, length,
-                       offset=self.data_offset + offset)
+                       offset=self.payload_file_offset + self.data_offset +
+                       offset)
 
   def Init(self):
     """Initializes the payload object.
@@ -194,6 +198,7 @@
     self.manifest_hasher = hashlib.sha256()
 
     # Read the file header.
+    self.payload_file.seek(self.payload_file_offset)
     self.header = self._ReadHeader()
 
     # Read the manifest.
@@ -215,6 +220,7 @@
   def Describe(self):
     """Emits the payload embedded description data to standard output."""
     def _DescribeImageInfo(description, image_info):
+      """Display info about the image."""
       def _DisplayIndentedValue(name, value):
         print('  {:<14} {}'.format(name+':', value))
 
@@ -245,7 +251,7 @@
 
   def ResetFile(self):
     """Resets the offset of the payload file to right past the manifest."""
-    self.payload_file.seek(self.data_offset)
+    self.payload_file.seek(self.payload_file_offset + self.data_offset)
 
   def IsDelta(self):
     """Returns True iff the payload appears to be a delta."""
diff --git a/update_attempter_android.cc b/update_attempter_android.cc
index e9502fe..0a1c20f 100644
--- a/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -32,6 +32,8 @@
 #include "update_engine/common/file_fetcher.h"
 #include "update_engine/common/utils.h"
 #include "update_engine/daemon_state_interface.h"
+#include "update_engine/metrics_reporter_android.h"
+#include "update_engine/metrics_utils.h"
 #include "update_engine/network_selector.h"
 #include "update_engine/payload_consumer/download_action.h"
 #include "update_engine/payload_consumer/filesystem_verifier_action.h"
@@ -86,7 +88,8 @@
       prefs_(prefs),
       boot_control_(boot_control),
       hardware_(hardware),
-      processor_(new ActionProcessor()) {
+      processor_(new ActionProcessor()),
+      metrics_reporter_(new MetricsReporterAndroid()) {
   network_selector_ = network::CreateNetworkSelector();
 }
 
@@ -427,6 +430,30 @@
 
   for (auto observer : daemon_state_->service_observers())
     observer->SendPayloadApplicationComplete(error_code);
+
+  // TODO(xunchang): assign proper values to the metrics.
+  PayloadType payload_type = kNumPayloadTypes;
+  DownloadSource download_source = kNumDownloadSources;
+  metrics::AttemptResult attempt_result =
+      metrics_utils::GetAttemptResult(error_code);
+  TimeDelta duration;
+  TimeDelta duration_uptime;
+  // Report the android metrics when we terminate the update. Currently we are
+  // reporting error_code only.
+  metrics_reporter_->ReportUpdateAttemptMetrics(
+      nullptr,  // system_state
+      0,        // attempt_number
+      payload_type,
+      duration,
+      duration_uptime,
+      0,  // payload_size
+      0,  // payload_bytes_downloaded
+      0,  // payload_download_speed_bps
+      download_source,
+      attempt_result,
+      error_code,
+      metrics::DownloadErrorCode::kUnset,
+      metrics::ConnectionType::kUnset);
 }
 
 void UpdateAttempterAndroid::SetStatusAndNotify(UpdateStatus status) {
diff --git a/update_attempter_android.h b/update_attempter_android.h
index 167191e..a347e52 100644
--- a/update_attempter_android.h
+++ b/update_attempter_android.h
@@ -31,6 +31,7 @@
 #include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/prefs_interface.h"
 #include "update_engine/daemon_state_interface.h"
+#include "update_engine/metrics_reporter_interface.h"
 #include "update_engine/network_selector_interface.h"
 #include "update_engine/payload_consumer/download_action.h"
 #include "update_engine/payload_consumer/postinstall_runner_action.h"
@@ -162,6 +163,8 @@
   // before applying an update to the other slot.
   bool updated_boot_flags_ = false;
 
+  std::unique_ptr<MetricsReporterInterface> metrics_reporter_;
+
   DISALLOW_COPY_AND_ASSIGN(UpdateAttempterAndroid);
 };
 
diff --git a/update_engine.gyp b/update_engine.gyp
index 32c6553..ba4cfff 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -523,6 +523,7 @@
             'fake_system_state.cc',
             'hardware_chromeos_unittest.cc',
             'image_properties_chromeos_unittest.cc',
+            'metrics_reporter_omaha_unittest.cc',
             'metrics_utils_unittest.cc',
             'omaha_request_action_unittest.cc',
             'omaha_request_params_unittest.cc',