Merge commit '51871ea530e88c90cd761a228f96372cae96abeb' into HEAD

Change-Id: Ie83699c8947c9260c02a2b9653d75bb0dd06468c
diff --git a/Android.mk b/Android.mk
index cc06643..6a23168 100644
--- a/Android.mk
+++ b/Android.mk
@@ -14,6 +14,8 @@
 # limitations under the License.
 #
 
+ifneq ($(TARGET_BUILD_PDK),true)
+
 LOCAL_PATH := $(my-dir)
 
 # Default values for the USE flags. Override these USE flags from your product
@@ -82,6 +84,7 @@
 generated_sources_dir := $(call local-generated-sources-dir)
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(generated_sources_dir)/proto/system
 LOCAL_SRC_FILES := $(ue_update_metadata_protos_src_files)
+LOCAL_CFLAGS := -Wall -Werror
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 # Build for the target.
@@ -91,6 +94,7 @@
 generated_sources_dir := $(call local-generated-sources-dir)
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(generated_sources_dir)/proto/system
 LOCAL_SRC_FILES := $(ue_update_metadata_protos_src_files)
+LOCAL_CFLAGS := -Wall -Werror
 include $(BUILD_STATIC_LIBRARY)
 
 # libpayload_consumer (type: static_library)
@@ -101,6 +105,7 @@
     libxz \
     libbz \
     libbspatch \
+    libbrotli \
     $(ue_update_metadata_protos_exported_static_libraries)
 ue_libpayload_consumer_exported_shared_libraries := \
     libcrypto \
@@ -466,7 +471,7 @@
 LOCAL_SRC_FILES := \
     boot_control_recovery.cc \
     hardware_android.cc \
-    metrics_reporter_android.cc \
+    metrics_reporter_stub.cc \
     metrics_utils.cc \
     network_selector_stub.cc \
     proxy_resolver.cc \
@@ -599,6 +604,7 @@
     libbsdiff \
     libdivsufsort \
     libdivsufsort64 \
+    libbrotli \
     libpayload_consumer \
     liblzma \
     update_metadata-protos \
@@ -1019,3 +1025,5 @@
     simg2img
 include $(BUILD_PREBUILT)
 endif  # HOST_OS == linux
+
+endif  # ifneq ($(TARGET_BUILD_PDK),true)
diff --git a/common/constants.cc b/common/constants.cc
index c0a6e27..5941c93 100644
--- a/common/constants.cc
+++ b/common/constants.cc
@@ -57,6 +57,7 @@
 const char kPrefsP2PFirstAttemptTimestamp[] = "p2p-first-attempt-timestamp";
 const char kPrefsP2PNumAttempts[] = "p2p-num-attempts";
 const char kPrefsPayloadAttemptNumber[] = "payload-attempt-number";
+const char kPrefsPostInstallSucceeded[] = "post-install-succeeded";
 const char kPrefsPreviousVersion[] = "previous-version";
 const char kPrefsResumedUpdateFailures[] = "resumed-update-failures";
 const char kPrefsRollbackVersion[] = "rollback-version";
@@ -103,5 +104,12 @@
 // This can be used to zero-rate OTA traffic by sending it over the correct
 // network.
 const char kPayloadPropertyNetworkId[] = "NETWORK_ID";
+// Set "SWITCH_SLOT_ON_REBOOT=0" to skip marking the updated partitions active.
+// The default is 1 (always switch slot if update succeeded).
+const char kPayloadPropertySwitchSlotOnReboot[] = "SWITCH_SLOT_ON_REBOOT";
+// Set "RUN_POST_INSTALL=0" to skip running post install, this will only be
+// honored if we're resuming an update and post install has already succeeded.
+// The default is 1 (always run post install).
+const char kPayloadPropertyRunPostInstall[] = "RUN_POST_INSTALL";
 
 }  // namespace chromeos_update_engine
diff --git a/common/constants.h b/common/constants.h
index 776e726..fefd08c 100644
--- a/common/constants.h
+++ b/common/constants.h
@@ -59,6 +59,7 @@
 extern const char kPrefsP2PFirstAttemptTimestamp[];
 extern const char kPrefsP2PNumAttempts[];
 extern const char kPrefsPayloadAttemptNumber[];
+extern const char kPrefsPostInstallSucceeded[];
 extern const char kPrefsPreviousVersion[];
 extern const char kPrefsResumedUpdateFailures[];
 extern const char kPrefsRollbackVersion[];
@@ -96,6 +97,8 @@
 extern const char kPayloadPropertyUserAgent[];
 extern const char kPayloadPropertyPowerwash[];
 extern const char kPayloadPropertyNetworkId[];
+extern const char kPayloadPropertySwitchSlotOnReboot[];
+extern const char kPayloadPropertyRunPostInstall[];
 
 // A download source is any combination of protocol and server (that's of
 // interest to us when looking at UMA metrics) using which we may download
diff --git a/common/error_code.h b/common/error_code.h
index e08ec46..adae391 100644
--- a/common/error_code.h
+++ b/common/error_code.h
@@ -73,6 +73,9 @@
   kFilesystemVerifierError = 47,
   kUserCanceled = 48,
   kNonCriticalUpdateInOOBE = 49,
+  // kOmahaUpdateIgnoredOverCellular = 50,
+  // kPayloadTimestampError = 51,
+  kUpdatedButNotActive = 52,
 
   // VERY IMPORTANT! When adding new error codes:
   //
diff --git a/common/error_code_utils.cc b/common/error_code_utils.cc
index ad4aeeb..983d004 100644
--- a/common/error_code_utils.cc
+++ b/common/error_code_utils.cc
@@ -144,8 +144,10 @@
       return "ErrorCode::kUserCanceled";
     case ErrorCode::kNonCriticalUpdateInOOBE:
       return "ErrorCode::kNonCriticalUpdateInOOBE";
-    // Don't add a default case to let the compiler warn about newly added
-    // error codes which should be added here.
+    case ErrorCode::kUpdatedButNotActive:
+      return "ErrorCode::kUpdatedButNotActive";
+      // Don't add a default case to let the compiler warn about newly added
+      // error codes which should be added here.
   }
 
   return "Unknown error: " + base::UintToString(static_cast<unsigned>(code));
diff --git a/common/test_utils.cc b/common/test_utils.cc
index fb22c80..e410283 100644
--- a/common/test_utils.cc
+++ b/common/test_utils.cc
@@ -24,6 +24,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/ioctl.h>
+#include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/sysmacros.h>
 #include <sys/types.h>
@@ -201,6 +202,13 @@
   device_info.lo_file_name[LO_NAME_SIZE - 1] = '\0';
   TEST_AND_RETURN_FALSE_ERRNO(
       ioctl(loop_device_fd, LOOP_SET_STATUS64, &device_info) == 0);
+  if (writable) {
+    // Make sure loop device isn't read only.
+    int ro = 0;
+    if (ioctl(loop_device_fd, BLKROSET, &ro) != 0) {
+      PLOG(WARNING) << "Failed to mark loop device writable.";
+    }
+  }
   return true;
 }
 
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/image_properties.h b/image_properties.h
index 4f94eeb..2d1a408 100644
--- a/image_properties.h
+++ b/image_properties.h
@@ -41,6 +41,9 @@
   // The system version of this image.
   std::string system_version;
 
+  // The version of all product components in key values pairs.
+  std::string product_components;
+
   // A unique string that identifies this build. Normally a combination of the
   // the version, signing keys and build target.
   std::string build_fingerprint;
diff --git a/image_properties_android.cc b/image_properties_android.cc
index 886a6b6..608bca7 100644
--- a/image_properties_android.cc
+++ b/image_properties_android.cc
@@ -27,6 +27,7 @@
 #include "update_engine/common/constants.h"
 #include "update_engine/common/platform_constants.h"
 #include "update_engine/common/prefs_interface.h"
+#include "update_engine/common/utils.h"
 #include "update_engine/system_state.h"
 
 using android::base::GetProperty;
@@ -42,6 +43,10 @@
 const char kSystemId[] = "system_id";
 const char kSystemVersion[] = "system_version";
 
+// The path to the product_components file which stores the version of each
+// components in OEM partition.
+const char kProductComponentsPath[] = "/oem/os-release.d/product_components";
+
 // Prefs used to store the target channel and powerwash settings.
 const char kPrefsImgPropChannelName[] = "img-prop-channel-name";
 const char kPrefsImgPropPowerwashAllowed[] = "img-prop-powerwash-allowed";
@@ -97,6 +102,8 @@
   result.version = GetStringWithDefault(osrelease, kProductVersion, "0.0.0.0");
   result.system_version =
       GetStringWithDefault(osrelease, kSystemVersion, "0.0.0.0");
+  // Can't read it with OsReleaseReader because it has multiple lines.
+  utils::ReadFile(kProductComponentsPath, &result.product_components);
 
   result.board = GetProperty(kPropProductName, "brillo");
   result.build_fingerprint = GetProperty(kPropBuildFingerprint, "none");
diff --git a/main.cc b/main.cc
index 91805ae..0b96307 100644
--- a/main.cc
+++ b/main.cc
@@ -89,7 +89,7 @@
   string log_file;
   if (log_to_file) {
 #ifdef __ANDROID__
-    log_file = "/data/misc/update_engine/update_engine.log";
+    log_file = "/data/misc/update_engine_log/update_engine.log";
     log_settings.delete_old = logging::DELETE_OLD_LOG_FILE;
 #else
     log_file = SetupLogFile("/var/log");
@@ -98,6 +98,12 @@
     log_settings.log_file = log_file.c_str();
   }
   logging::InitLogging(log_settings);
+
+#ifdef __ANDROID__
+  // The log file will have AID_LOG as group ID; this GID is inherited from the
+  // parent directory "/data/misc/update_engine_log" which sets the SGID bit.
+  chmod(log_file.c_str(), 0640);
+#endif
 }
 
 }  // namespace
diff --git a/metrics_constants.h b/metrics_constants.h
index f6d0c74..abec2ad 100644
--- a/metrics_constants.h
+++ b/metrics_constants.h
@@ -95,6 +95,8 @@
   kPostInstallFailed,           // The postinstall step failed.
   kAbnormalTermination,         // The attempt ended abnormally.
   kUpdateCanceled,              // Update canceled by the user.
+  kUpdateSucceededNotActive,    // Update succeeded but the new slot is not
+                                // active.
 
   kNumConstants,
 
diff --git a/metrics_reporter_android.cc b/metrics_reporter_android.cc
index 4a57918..26aa057 100644
--- a/metrics_reporter_android.cc
+++ b/metrics_reporter_android.cc
@@ -16,33 +16,143 @@
 
 #include "update_engine/metrics_reporter_android.h"
 
-#ifndef _UE_SIDELOAD
+#include <memory>
+#include <string>
+
 #include <metricslogger/metrics_logger.h>
-#endif  // _UE_SIDELOAD
+
+#include "update_engine/common/constants.h"
+
+namespace {
+void LogHistogram(const std::string& metrics, int value) {
+  android::metricslogger::LogHistogram(metrics, value);
+  LOG(INFO) << "uploading " << value << "to histogram for metric " << metrics;
+}
+}  // namespace
 
 namespace chromeos_update_engine {
 
 namespace metrics {
 
-#ifndef _UE_SIDELOAD
-const char kMetricsUpdateEngineErrorCode[] = "ota_update_engine_error_code";
-#endif
+// The histograms are defined in:
+// depot/google3/analysis/uma/configs/clearcut/TRON/histograms.xml
+constexpr char kMetricsUpdateEngineAttemptNumber[] =
+    "ota_update_engine_attempt_count";
+constexpr char kMetricsUpdateEngineAttemptResult[] =
+    "ota_update_engine_attempt_result";
+constexpr char kMetricsUpdateEngineAttemptDurationInMinutes[] =
+    "ota_update_engine_attempt_duration_boottime_in_minutes";
+constexpr char kMetricsUpdateEngineAttemptDurationUptimeInMinutes[] =
+    "ota_update_engine_attempt_duration_monotonic_in_minutes";
+constexpr char kMetricsUpdateEngineAttemptErrorCode[] =
+    "ota_update_engine_attempt_error_code";
+constexpr char kMetricsUpdateEngineAttemptPayloadSizeMiB[] =
+    "ota_update_engine_attempt_payload_size_mib";
+constexpr char kMetricsUpdateEngineAttemptPayloadType[] =
+    "ota_update_engine_attempt_payload_type";
+constexpr char kMetricsUpdateEngineAttemptCurrentBytesDownloadedMiB[] =
+    "ota_update_engine_attempt_current_bytes_downloaded_mib";
+
+constexpr char kMetricsUpdateEngineSuccessfulUpdateAttemptCount[] =
+    "ota_update_engine_successful_update_attempt_count";
+constexpr char kMetricsUpdateEngineSuccessfulUpdateTotalDurationInMinutes[] =
+    "ota_update_engine_successful_update_total_duration_in_minutes";
+constexpr char kMetricsUpdateEngineSuccessfulUpdatePayloadSizeMiB[] =
+    "ota_update_engine_successful_update_payload_size_mib";
+constexpr char kMetricsUpdateEngineSuccessfulUpdatePayloadType[] =
+    "ota_update_engine_successful_update_payload_type";
+constexpr char kMetricsUpdateEngineSuccessfulUpdateRebootCount[] =
+    "ota_update_engine_successful_update_reboot_count";
+constexpr char kMetricsUpdateEngineSuccessfulUpdateTotalBytesDownloadedMiB[] =
+    "ota_update_engine_successful_update_total_bytes_downloaded_mib";
+constexpr char
+    kMetricsUpdateEngineSuccessfulUpdateDownloadOverheadPercentage[] =
+        "ota_update_engine_successful_update_download_overhead_percentage";
+
+std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter() {
+  return std::make_unique<MetricsReporterAndroid>();
+}
+
 }  // 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 */,
-    metrics::AttemptResult /* attempt_result */,
+    int attempt_number,
+    PayloadType payload_type,
+    base::TimeDelta duration,
+    base::TimeDelta duration_uptime,
+    int64_t payload_size,
+    metrics::AttemptResult attempt_result,
     ErrorCode error_code) {
-// No need to log histogram under sideload mode.
-#ifndef _UE_SIDELOAD
-  android::metricslogger::LogHistogram(metrics::kMetricsUpdateEngineErrorCode,
-                                       static_cast<int>(error_code));
-#endif
+  LogHistogram(metrics::kMetricsUpdateEngineAttemptNumber, attempt_number);
+  LogHistogram(metrics::kMetricsUpdateEngineAttemptPayloadType,
+               static_cast<int>(payload_type));
+  LogHistogram(metrics::kMetricsUpdateEngineAttemptDurationInMinutes,
+               duration.InMinutes());
+  LogHistogram(metrics::kMetricsUpdateEngineAttemptDurationUptimeInMinutes,
+               duration_uptime.InMinutes());
+
+  int64_t payload_size_mib = payload_size / kNumBytesInOneMiB;
+  LogHistogram(metrics::kMetricsUpdateEngineAttemptPayloadSizeMiB,
+               payload_size_mib);
+
+  LogHistogram(metrics::kMetricsUpdateEngineAttemptResult,
+               static_cast<int>(attempt_result));
+  LogHistogram(metrics::kMetricsUpdateEngineAttemptErrorCode,
+               static_cast<int>(error_code));
+}
+
+void MetricsReporterAndroid::ReportUpdateAttemptDownloadMetrics(
+    int64_t payload_bytes_downloaded,
+    int64_t /* payload_download_speed_bps */,
+    DownloadSource /* download_source */,
+    metrics::DownloadErrorCode /* payload_download_error_code */,
+    metrics::ConnectionType /* connection_type */) {
+  LogHistogram(metrics::kMetricsUpdateEngineAttemptCurrentBytesDownloadedMiB,
+               payload_bytes_downloaded);
+}
+
+void MetricsReporterAndroid::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 */) {
+  LogHistogram(metrics::kMetricsUpdateEngineSuccessfulUpdateAttemptCount,
+               attempt_count);
+  LogHistogram(metrics::kMetricsUpdateEngineSuccessfulUpdatePayloadType,
+               static_cast<int>(payload_type));
+
+  int64_t payload_size_mib = payload_size / kNumBytesInOneMiB;
+  LogHistogram(metrics::kMetricsUpdateEngineSuccessfulUpdatePayloadSizeMiB,
+               payload_size_mib);
+
+  int64_t total_bytes_downloaded = 0;
+  for (size_t i = 0; i < kNumDownloadSources; i++) {
+    total_bytes_downloaded += num_bytes_downloaded[i] / kNumBytesInOneMiB;
+  }
+  LogHistogram(
+      metrics::kMetricsUpdateEngineSuccessfulUpdateTotalBytesDownloadedMiB,
+      total_bytes_downloaded);
+  LogHistogram(
+      metrics::kMetricsUpdateEngineSuccessfulUpdateDownloadOverheadPercentage,
+      download_overhead_percentage);
+
+  LogHistogram(
+      metrics::kMetricsUpdateEngineSuccessfulUpdateTotalDurationInMinutes,
+      total_duration.InMinutes());
+  LogHistogram(metrics::kMetricsUpdateEngineSuccessfulUpdateRebootCount,
+               reboot_count);
+}
+
+void MetricsReporterAndroid::ReportAbnormallyTerminatedUpdateAttemptMetrics() {
+  int attempt_result =
+      static_cast<int>(metrics::AttemptResult::kAbnormalTermination);
+  LogHistogram(metrics::kMetricsUpdateEngineAttemptResult, attempt_result);
 }
 
 };  // namespace chromeos_update_engine
diff --git a/metrics_reporter_android.h b/metrics_reporter_android.h
index b83e651..ee94e43 100644
--- a/metrics_reporter_android.h
+++ b/metrics_reporter_android.h
@@ -55,9 +55,9 @@
       int64_t payload_download_speed_bps,
       DownloadSource download_source,
       metrics::DownloadErrorCode payload_download_error_code,
-      metrics::ConnectionType connection_type) override {}
+      metrics::ConnectionType connection_type) override;
 
-  void ReportAbnormallyTerminatedUpdateAttemptMetrics() override {}
+  void ReportAbnormallyTerminatedUpdateAttemptMetrics() override;
 
   void ReportSuccessfulUpdateMetrics(
       int attempt_count,
@@ -68,7 +68,7 @@
       int download_overhead_percentage,
       base::TimeDelta total_duration,
       int reboot_count,
-      int url_switch_count) override {}
+      int url_switch_count) override;
 
   void ReportCertificateCheckMetrics(ServerToCheck server_to_check,
                                      CertificateCheckResult result) override {}
diff --git a/metrics_reporter_interface.h b/metrics_reporter_interface.h
index bc9d387..2c7ce5b 100644
--- a/metrics_reporter_interface.h
+++ b/metrics_reporter_interface.h
@@ -17,6 +17,8 @@
 #ifndef UPDATE_ENGINE_METRICS_REPORTER_INTERFACE_H_
 #define UPDATE_ENGINE_METRICS_REPORTER_INTERFACE_H_
 
+#include <memory>
+
 #include <base/time/time.h>
 
 #include "update_engine/common/constants.h"
@@ -29,6 +31,12 @@
 enum class ServerToCheck;
 enum class CertificateCheckResult;
 
+namespace metrics {
+
+std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter();
+
+}  // namespace metrics
+
 class MetricsReporterInterface {
  public:
   virtual ~MetricsReporterInterface() = default;
diff --git a/metrics_reporter_omaha.cc b/metrics_reporter_omaha.cc
index 5f24cbf..0397b83 100644
--- a/metrics_reporter_omaha.cc
+++ b/metrics_reporter_omaha.cc
@@ -112,6 +112,10 @@
     "UpdateEngine.InstallDateProvisioningSource";
 const char kMetricTimeToRebootMinutes[] = "UpdateEngine.TimeToRebootMinutes";
 
+std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter() {
+  return std::make_unique<MetricsReporterOmaha>();
+}
+
 }  // namespace metrics
 
 MetricsReporterOmaha::MetricsReporterOmaha()
diff --git a/metrics_reporter_stub.cc b/metrics_reporter_stub.cc
new file mode 100644
index 0000000..81664a5
--- /dev/null
+++ b/metrics_reporter_stub.cc
@@ -0,0 +1,31 @@
+//
+// 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_stub.h"
+
+#include <memory>
+
+namespace chromeos_update_engine {
+
+namespace metrics {
+
+std::unique_ptr<MetricsReporterInterface> CreateMetricsReporter() {
+  return std::make_unique<MetricsReporterStub>();
+}
+
+}  // namespace metrics
+
+}  // namespace chromeos_update_engine
diff --git a/metrics_reporter_stub.h b/metrics_reporter_stub.h
new file mode 100644
index 0000000..d0f75ab
--- /dev/null
+++ b/metrics_reporter_stub.h
@@ -0,0 +1,88 @@
+//
+// 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_STUB_H_
+#define UPDATE_ENGINE_METRICS_REPORTER_STUB_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 MetricsReporterStub : public MetricsReporterInterface {
+ public:
+  MetricsReporterStub() = default;
+
+  ~MetricsReporterStub() 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,
+                                  metrics::AttemptResult attempt_result,
+                                  ErrorCode internal_error_code) override {}
+
+  void ReportUpdateAttemptDownloadMetrics(
+      int64_t payload_bytes_downloaded,
+      int64_t payload_download_speed_bps,
+      DownloadSource download_source,
+      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(MetricsReporterStub);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_METRICS_REPORTER_STUB_H_
diff --git a/metrics_utils.cc b/metrics_utils.cc
index 5cff293..7e6b20f 100644
--- a/metrics_utils.cc
+++ b/metrics_utils.cc
@@ -39,6 +39,9 @@
     case ErrorCode::kSuccess:
       return metrics::AttemptResult::kUpdateSucceeded;
 
+    case ErrorCode::kUpdatedButNotActive:
+      return metrics::AttemptResult::kUpdateSucceededNotActive;
+
     case ErrorCode::kDownloadTransferError:
       return metrics::AttemptResult::kPayloadDownloadError;
 
@@ -210,6 +213,7 @@
     case ErrorCode::kOmahaRequestXMLHasEntityDecl:
     case ErrorCode::kFilesystemVerifierError:
     case ErrorCode::kUserCanceled:
+    case ErrorCode::kUpdatedButNotActive:
       break;
 
     // Special flags. These can't happen (we mask them out above) but
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index 7e41f94..4332702 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -31,6 +31,7 @@
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
 #include <base/time/time.h>
+#include <brillo/key_value_store.h>
 #include <expat.h>
 #include <metrics/metrics_library.h>
 
@@ -207,8 +208,17 @@
 struct OmahaAppData {
   string id;
   string version;
+  string product_components;
 };
 
+bool IsValidComponentID(const string& id) {
+  for (char c : id) {
+    if (!isalnum(c) && c != '-' && c != '_' && c != '.')
+      return false;
+  }
+  return true;
+}
+
 // Returns an XML that corresponds to the entire <app> node of the Omaha
 // request based on the given parameters.
 string GetAppXml(const OmahaEvent* event,
@@ -276,11 +286,39 @@
                     XmlEncodeWithDefault(params->os_build_type(), "") + "\" ";
   }
 
+  string product_components_args;
+  if (!app_data.product_components.empty()) {
+    brillo::KeyValueStore store;
+    if (store.LoadFromString(app_data.product_components)) {
+      for (const string& key : store.GetKeys()) {
+        if (!IsValidComponentID(key)) {
+          LOG(ERROR) << "Invalid component id: " << key;
+          continue;
+        }
+        string version;
+        if (!store.GetString(key, &version)) {
+          LOG(ERROR) << "Failed to get version for " << key
+                     << " in product_components.";
+          continue;
+        }
+        product_components_args +=
+            base::StringPrintf("_%s.version=\"%s\" ",
+                               key.c_str(),
+                               XmlEncodeWithDefault(version, "").c_str());
+      }
+    } else {
+      LOG(ERROR) << "Failed to parse product_components:\n"
+                 << app_data.product_components;
+    }
+  }
+
+  // clang-format off
   string app_xml = "    <app "
       "appid=\"" + XmlEncodeWithDefault(app_data.id, "") + "\" " +
       app_cohort_args +
       app_versions +
       app_channels +
+      product_components_args +
       fingerprint_arg +
       buildtype_arg +
       "lang=\"" + XmlEncodeWithDefault(params->app_lang(), "en-US") + "\" " +
@@ -293,7 +331,7 @@
       ">\n" +
          app_body +
       "    </app>\n";
-
+  // clang-format on
   return app_xml;
 }
 
@@ -319,8 +357,10 @@
                      int install_date_in_days,
                      SystemState* system_state) {
   string os_xml = GetOsXml(params);
-  OmahaAppData product_app = {.id = params->GetAppId(),
-                              .version = params->app_version()};
+  OmahaAppData product_app = {
+      .id = params->GetAppId(),
+      .version = params->app_version(),
+      .product_components = params->product_components()};
   string app_xml = GetAppXml(event,
                              params,
                              product_app,
diff --git a/omaha_request_params.h b/omaha_request_params.h
index ba7f2c3..73edd6f 100644
--- a/omaha_request_params.h
+++ b/omaha_request_params.h
@@ -130,6 +130,9 @@
   inline std::string system_version() const {
     return image_props_.system_version;
   }
+  inline std::string product_components() const {
+    return image_props_.product_components;
+  }
 
   inline std::string current_channel() const {
     return image_props_.current_channel;
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index d6ac16c..d384e45 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -24,6 +24,7 @@
 #include <cstring>
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include <base/files/file_util.h>
@@ -975,8 +976,8 @@
     for (uint64_t offset = 0; offset < length; offset += zeros.size()) {
       uint64_t chunk_length = min(length - offset,
                                   static_cast<uint64_t>(zeros.size()));
-      TEST_AND_RETURN_FALSE(
-          utils::PWriteAll(target_fd_, zeros.data(), chunk_length, start + offset));
+      TEST_AND_RETURN_FALSE(utils::PWriteAll(
+          target_fd_, zeros.data(), chunk_length, start + offset));
     }
   }
   return true;
@@ -1152,8 +1153,8 @@
     const uint64_t begin_byte =
         end_byte - (block_size_ - operation.dst_length() % block_size_);
     brillo::Blob zeros(end_byte - begin_byte);
-    TEST_AND_RETURN_FALSE(
-        utils::PWriteAll(target_fd_, zeros.data(), end_byte - begin_byte, begin_byte));
+    TEST_AND_RETURN_FALSE(utils::PWriteAll(
+        target_fd_, zeros.data(), end_byte - begin_byte, begin_byte));
   }
   return true;
 }
@@ -1629,6 +1630,7 @@
     prefs->SetInt64(kPrefsManifestMetadataSize, -1);
     prefs->SetInt64(kPrefsManifestSignatureSize, -1);
     prefs->SetInt64(kPrefsResumedUpdateFailures, 0);
+    prefs->Delete(kPrefsPostInstallSucceeded);
   }
   return true;
 }
diff --git a/payload_consumer/install_plan.cc b/payload_consumer/install_plan.cc
index b0dff31..45112d6 100644
--- a/payload_consumer/install_plan.cc
+++ b/payload_consumer/install_plan.cc
@@ -87,7 +87,10 @@
             << ", url: " << download_url << payloads_str << partitions_str
             << ", hash_checks_mandatory: "
             << utils::ToString(hash_checks_mandatory)
-            << ", powerwash_required: " << utils::ToString(powerwash_required);
+            << ", powerwash_required: " << utils::ToString(powerwash_required)
+            << ", switch_slot_on_reboot: "
+            << utils::ToString(switch_slot_on_reboot)
+            << ", run_post_install: " << utils::ToString(run_post_install);
 }
 
 bool InstallPlan::LoadPartitionsFromSlots(BootControlInterface* boot_control) {
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index 551f8c2..5cdfbc1 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -119,6 +119,14 @@
   // False otherwise.
   bool powerwash_required{false};
 
+  // True if the updated slot should be marked active on success.
+  // False otherwise.
+  bool switch_slot_on_reboot{true};
+
+  // True if the update should run its post-install step.
+  // False otherwise.
+  bool run_post_install{true};
+
   // If not blank, a base-64 encoded representation of the PEM-encoded
   // public key in the response.
   std::string public_key_rsa;
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index 27a9ed6..cedecda 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -82,6 +82,11 @@
 }
 
 void PostinstallRunnerAction::PerformPartitionPostinstall() {
+  if (!install_plan_.run_post_install) {
+    LOG(INFO) << "Skipping post-install according to install plan.";
+    return CompletePostinstall(ErrorCode::kSuccess);
+  }
+
   if (install_plan_.download_url.empty()) {
     LOG(INFO) << "Skipping post-install during rollback";
     return CompletePostinstall(ErrorCode::kSuccess);
@@ -331,15 +336,21 @@
 void PostinstallRunnerAction::CompletePostinstall(ErrorCode error_code) {
   // We only attempt to mark the new slot as active if all the postinstall
   // steps succeeded.
-  if (error_code == ErrorCode::kSuccess &&
-      !boot_control_->SetActiveBootSlot(install_plan_.target_slot)) {
-    error_code = ErrorCode::kPostinstallRunnerError;
+  if (error_code == ErrorCode::kSuccess) {
+    if (install_plan_.switch_slot_on_reboot) {
+      if (!boot_control_->SetActiveBootSlot(install_plan_.target_slot)) {
+        error_code = ErrorCode::kPostinstallRunnerError;
+      }
+    } else {
+      error_code = ErrorCode::kUpdatedButNotActive;
+    }
   }
 
   ScopedActionCompleter completer(processor_, this);
   completer.set_code(error_code);
 
-  if (error_code != ErrorCode::kSuccess) {
+  if (error_code != ErrorCode::kSuccess &&
+      error_code != ErrorCode::kUpdatedButNotActive) {
     LOG(ERROR) << "Postinstall action failed.";
 
     // Undo any changes done to trigger Powerwash.
diff --git a/payload_state.cc b/payload_state.cc
index 1ec32c5..cff02b1 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -357,6 +357,7 @@
     case ErrorCode::kOmahaRequestXMLHasEntityDecl:
     case ErrorCode::kFilesystemVerifierError:
     case ErrorCode::kUserCanceled:
+    case ErrorCode::kUpdatedButNotActive:
       LOG(INFO) << "Not incrementing URL index or failure count for this error";
       break;
 
@@ -633,6 +634,7 @@
     case metrics::AttemptResult::kPostInstallFailed:
     case metrics::AttemptResult::kAbnormalTermination:
     case metrics::AttemptResult::kUpdateCanceled:
+    case metrics::AttemptResult::kUpdateSucceededNotActive:
     case metrics::AttemptResult::kNumConstants:
     case metrics::AttemptResult::kUnset:
       break;
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/scripts/brillo_update_payload b/scripts/brillo_update_payload
index 868e723..61ecadf 100755
--- a/scripts/brillo_update_payload
+++ b/scripts/brillo_update_payload
@@ -397,7 +397,8 @@
     if grep -v -E '^[a-zA-Z0-9_-]*$' "${ab_partitions_list}" >&2; then
       die "Invalid partition names found in the partition list."
     fi
-    partitions=($(cat "${ab_partitions_list}"))
+    # Get partition list without duplicates.
+    partitions=($(awk '!seen[$0]++' "${ab_partitions_list}"))
     if [[ ${#partitions[@]} -eq 0 ]]; then
       die "The list of partitions is empty. Can't generate a payload."
     fi
diff --git a/scripts/update_device.py b/scripts/update_device.py
index 1a0daf8..64cfbe3 100755
--- a/scripts/update_device.py
+++ b/scripts/update_device.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python2
 #
 # Copyright (C) 2017 The Android Open Source Project
 #
@@ -276,14 +276,13 @@
   return t
 
 
-def AndroidUpdateCommand(ota_filename, payload_url):
+def AndroidUpdateCommand(ota_filename, payload_url, extra_headers):
   """Return the command to run to start the update in the Android device."""
   ota = AndroidOTAPackage(ota_filename)
   headers = ota.properties
   headers += 'USER_AGENT=Dalvik (something, something)\n'
-
-  # headers += 'POWERWASH=1\n'
   headers += 'NETWORK_ID=0\n'
+  headers += extra_headers
 
   return ['update_engine_client', '--update', '--follow',
           '--payload=%s' % payload_url, '--offset=%d' % ota.offset,
@@ -360,6 +359,8 @@
                       help='Less verbose output')
   parser.add_argument('--public-key', type=str, default='',
                       help='Override the public key used to verify payload.')
+  parser.add_argument('--extra-headers', type=str, default='',
+                      help='Extra headers to pass to the device.')
   args = parser.parse_args()
   logging.basicConfig(
       level=logging.WARNING if args.no_verbose else logging.INFO)
@@ -382,7 +383,12 @@
     device_ota_file = os.path.join(OTA_PACKAGE_PATH, 'debug.zip')
     payload_url = 'file://' + device_ota_file
     if not args.no_push:
-      cmds.append(['push', args.otafile, device_ota_file])
+      data_local_tmp_file = '/data/local/tmp/debug.zip'
+      cmds.append(['push', args.otafile, data_local_tmp_file])
+      cmds.append(['shell', 'su', '0', 'mv', data_local_tmp_file,
+                   device_ota_file])
+      cmds.append(['shell', 'su', '0', 'chcon',
+                   'u:object_r:ota_package_file:s0', device_ota_file])
     cmds.append(['shell', 'su', '0', 'chown', 'system:cache', device_ota_file])
     cmds.append(['shell', 'su', '0', 'chmod', '0660', device_ota_file])
   else:
@@ -418,7 +424,8 @@
       update_cmd = \
           OmahaUpdateCommand('http://127.0.0.1:%d/update' % DEVICE_PORT)
     else:
-      update_cmd = AndroidUpdateCommand(args.otafile, payload_url)
+      update_cmd = \
+          AndroidUpdateCommand(args.otafile, payload_url, args.extra_headers)
     cmds.append(['shell', 'su', '0'] + update_cmd)
 
     for cmd in cmds:
diff --git a/update_attempter_android.cc b/update_attempter_android.cc
index 22bb4c2..e716296 100644
--- a/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -34,7 +34,7 @@
 #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_reporter_interface.h"
 #include "update_engine/metrics_utils.h"
 #include "update_engine/network_selector.h"
 #include "update_engine/payload_consumer/download_action.h"
@@ -80,6 +80,13 @@
   return false;
 }
 
+bool GetHeaderAsBool(const string& header, bool default_value) {
+  int value = 0;
+  if (base::StringToInt(header, &value) && (value == 0 || value == 1))
+    return value == 1;
+  return default_value;
+}
+
 }  // namespace
 
 UpdateAttempterAndroid::UpdateAttempterAndroid(
@@ -92,8 +99,8 @@
       boot_control_(boot_control),
       hardware_(hardware),
       processor_(new ActionProcessor()),
-      clock_(new Clock()),
-      metrics_reporter_(new MetricsReporterAndroid()) {
+      clock_(new Clock()) {
+  metrics_reporter_ = metrics::CreateMetricsReporter();
   network_selector_ = network::CreateNetworkSelector();
 }
 
@@ -192,10 +199,25 @@
   install_plan_.source_slot = boot_control_->GetCurrentSlot();
   install_plan_.target_slot = install_plan_.source_slot == 0 ? 1 : 0;
 
-  int data_wipe = 0;
   install_plan_.powerwash_required =
-      base::StringToInt(headers[kPayloadPropertyPowerwash], &data_wipe) &&
-      data_wipe != 0;
+      GetHeaderAsBool(headers[kPayloadPropertyPowerwash], false);
+
+  install_plan_.switch_slot_on_reboot =
+      GetHeaderAsBool(headers[kPayloadPropertySwitchSlotOnReboot], true);
+
+  install_plan_.run_post_install = true;
+  // Optionally skip post install if and only if:
+  // a) we're resuming
+  // b) post install has already succeeded before
+  // c) RUN_POST_INSTALL is set to 0.
+  if (install_plan_.is_resume && prefs_->Exists(kPrefsPostInstallSucceeded)) {
+    bool post_install_succeeded = false;
+    prefs_->GetBoolean(kPrefsPostInstallSucceeded, &post_install_succeeded);
+    if (post_install_succeeded) {
+      install_plan_.run_post_install =
+          GetHeaderAsBool(headers[kPayloadPropertyRunPostInstall], true);
+    }
+  }
 
   NetworkId network_id = kDefaultNetworkId;
   if (!headers[kPayloadPropertyNetworkId].empty()) {
@@ -313,7 +335,6 @@
       // Update succeeded.
       WriteUpdateCompletedMarker();
       prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0);
-      DeltaPerformer::ResetUpdateProgress(prefs_, false);
 
       LOG(INFO) << "Update successfully applied, waiting to reboot.";
       break;
@@ -351,6 +372,11 @@
   if (type == DownloadAction::StaticType()) {
     download_progress_ = 0;
   }
+  if (type == PostinstallRunnerAction::StaticType()) {
+    bool succeeded =
+        code == ErrorCode::kSuccess || code == ErrorCode::kUpdatedButNotActive;
+    prefs_->SetBoolean(kPrefsPostInstallSucceeded, succeeded);
+  }
   if (code != ErrorCode::kSuccess) {
     // If an action failed, the ActionProcessor will cancel the whole thing.
     return;
@@ -372,6 +398,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 +482,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);
   }
 }
 
@@ -557,31 +595,57 @@
       metrics_utils::GetAttemptResult(error_code);
   Time attempt_start_time = Time::FromInternalValue(
       metrics_utils::GetPersistedValue(kPrefsUpdateTimestampStart, prefs_));
+  TimeDelta duration = clock_->GetBootTime() - attempt_start_time;
   TimeDelta duration_uptime = clock_->GetMonotonicTime() - attempt_start_time;
 
   metrics_reporter_->ReportUpdateAttemptMetrics(
       nullptr,  // system_state
       static_cast<int>(attempt_number),
       payload_type,
-      TimeDelta(),
+      duration,
       duration_uptime,
       payload_size,
       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
-        duration_uptime,
+        num_bytes_downloaded,
+        download_overhead_percentage,
+        duration,
         static_cast<int>(reboot_count),
         0);  // url_switch_count
   }
@@ -656,6 +720,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 6c0718a..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;
@@ -120,16 +122,19 @@
   prefs_.SetString(kPrefsPreviousVersion, "56789");
   prefs_.SetInt64(kPrefsUpdateTimestampStart, 12345);
 
-  Time now = Time::FromInternalValue(22345);
-  clock_->SetMonotonicTime(now);
-  TimeDelta duration = now - Time::FromInternalValue(12345);
+  Time boot_time = Time::FromInternalValue(22345);
+  Time up_time = Time::FromInternalValue(21345);
+  clock_->SetBootTime(boot_time);
+  clock_->SetMonotonicTime(up_time);
+  TimeDelta duration = boot_time - Time::FromInternalValue(12345);
+  TimeDelta duration_uptime = up_time - Time::FromInternalValue(12345);
   EXPECT_CALL(
       *metrics_reporter_,
       ReportUpdateAttemptMetrics(_,
                                  2,
                                  _,
-                                 _,
                                  duration,
+                                 duration_uptime,
                                  _,
                                  metrics::AttemptResult::kUpdateSucceeded,
                                  ErrorCode::kSuccess))
@@ -147,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
diff --git a/update_engine_client_android.cc b/update_engine_client_android.cc
index 989a97e..267f6e9 100644
--- a/update_engine_client_android.cc
+++ b/update_engine_client_android.cc
@@ -97,7 +97,10 @@
   ErrorCode code = static_cast<ErrorCode>(error_code);
   LOG(INFO) << "onPayloadApplicationComplete(" << utils::ErrorCodeToString(code)
             << " (" << error_code << "))";
-  client_->ExitWhenIdle(code == ErrorCode::kSuccess ? EX_OK : 1);
+  client_->ExitWhenIdle(
+      (code == ErrorCode::kSuccess || code == ErrorCode::kUpdatedButNotActive)
+          ? EX_OK
+          : 1);
   return Status::ok();
 }
 
diff --git a/update_manager/chromeos_policy.cc b/update_manager/chromeos_policy.cc
index ffd378c..44f8821 100644
--- a/update_manager/chromeos_policy.cc
+++ b/update_manager/chromeos_policy.cc
@@ -134,6 +134,7 @@
     case ErrorCode::kOmahaRequestXMLHasEntityDecl:
     case ErrorCode::kFilesystemVerifierError:
     case ErrorCode::kUserCanceled:
+    case ErrorCode::kUpdatedButNotActive:
       LOG(INFO) << "Not changing URL index or failure count due to error "
                 << chromeos_update_engine::utils::ErrorCodeToString(err_code)
                 << " (" << static_cast<int>(err_code) << ")";