Snap for 4585119 from d6183ebe202de48a022f962bb5d24d2bc359f815 to pi-release

Change-Id: I70872b5e26ca805830542eb795b53e463cd8f1ff
diff --git a/Android.mk b/Android.mk
index 5a3129e..d79d89b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -141,6 +141,7 @@
     payload_consumer/file_writer.cc \
     payload_consumer/filesystem_verifier_action.cc \
     payload_consumer/install_plan.cc \
+    payload_consumer/mount_history.cc \
     payload_consumer/payload_constants.cc \
     payload_consumer/payload_verifier.cc \
     payload_consumer/postinstall_runner_action.cc \
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index c4db0c7..a8b8a94 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -21,6 +21,7 @@
 #include <map>
 #include <sstream>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include <base/bind.h>
@@ -235,10 +236,12 @@
                                system_state->prefs());
   string app_versions;
 
-  // If we are upgrading to a more stable channel and we are allowed to do
+  // If we are downgrading to a more stable channel and we are allowed to do
   // powerwash, then pass 0.0.0.0 as the version. This is needed to get the
   // highest-versioned payload on the destination channel.
-  if (params->to_more_stable_channel() && params->is_powerwash_allowed()) {
+  bool is_potential_downgrade =
+      params->to_more_stable_channel() && params->is_powerwash_allowed();
+  if (is_potential_downgrade) {
     LOG(INFO) << "Passing OS version as 0.0.0.0 as we are set to powerwash "
               << "on downgrading to the version in the more stable channel";
     app_versions = "version=\"0.0.0.0\" from_version=\"" +
@@ -276,8 +279,9 @@
 
   string fingerprint_arg;
   if (!params->os_build_fingerprint().empty()) {
-    fingerprint_arg =
-        "fingerprint=\"" + XmlEncodeWithDefault(params->os_build_fingerprint(), "") + "\" ";
+    fingerprint_arg = "fingerprint=\"" +
+                      XmlEncodeWithDefault(params->os_build_fingerprint(), "") +
+                      "\" ";
   }
 
   string buildtype_arg;
@@ -287,7 +291,7 @@
   }
 
   string product_components_args;
-  if (!app_data.product_components.empty()) {
+  if (!is_potential_downgrade && !app_data.product_components.empty()) {
     brillo::KeyValueStore store;
     if (store.LoadFromString(app_data.product_components)) {
       for (const string& key : store.GetKeys()) {
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index d57abe5..87dc404 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -812,7 +812,7 @@
 TEST_F(OmahaRequestActionTest, SkipNonCriticalUpdatesBeforeOOBE) {
   OmahaResponse response;
 
-  // TODO set better default value for metrics::checkresult in
+  // TODO(senj): set better default value for metrics::checkresult in
   // OmahaRequestAction::ActionCompleted.
   fake_system_state_.fake_hardware()->UnsetIsOOBEComplete();
   ASSERT_FALSE(TestUpdateCheck(nullptr,  // request_params
@@ -2184,6 +2184,7 @@
   params.set_root(tempdir.GetPath().value());
   params.set_app_id("{22222222-2222-2222-2222-222222222222}");
   params.set_app_version("1.2.3.4");
+  params.set_product_components("o.bundle=1");
   params.set_current_channel("canary-channel");
   EXPECT_TRUE(params.SetTargetChannel("stable-channel", true, nullptr));
   params.UpdateDownloadChannel();
@@ -2205,6 +2206,7 @@
       "appid=\"{22222222-2222-2222-2222-222222222222}\" "
       "version=\"0.0.0.0\" from_version=\"1.2.3.4\" "
       "track=\"stable-channel\" from_track=\"canary-channel\" "));
+  EXPECT_EQ(string::npos, post_str.find("o.bundle"));
 }
 
 TEST_F(OmahaRequestActionTest, TestChangingToLessStableChannel) {
@@ -2217,6 +2219,7 @@
   params.set_root(tempdir.GetPath().value());
   params.set_app_id("{11111111-1111-1111-1111-111111111111}");
   params.set_app_version("5.6.7.8");
+  params.set_product_components("o.bundle=1");
   params.set_current_channel("stable-channel");
   EXPECT_TRUE(params.SetTargetChannel("canary-channel", false, nullptr));
   params.UpdateDownloadChannel();
@@ -2239,6 +2242,7 @@
       "version=\"5.6.7.8\" "
       "track=\"canary-channel\" from_track=\"stable-channel\""));
   EXPECT_EQ(string::npos, post_str.find("from_version"));
+  EXPECT_NE(string::npos, post_str.find("o.bundle.version=\"1\""));
 }
 
 // Checks that the initial ping with a=-1 r=-1 is not send when the device
diff --git a/omaha_request_params.h b/omaha_request_params.h
index 6284ee1..a9215ae 100644
--- a/omaha_request_params.h
+++ b/omaha_request_params.h
@@ -133,6 +133,9 @@
   inline std::string product_components() const {
     return image_props_.product_components;
   }
+  inline void set_product_components(const std::string& product_components) {
+    image_props_.product_components = 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 45ad58b..d5ae3e6 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -50,6 +50,7 @@
 #include "update_engine/payload_consumer/extent_reader.h"
 #include "update_engine/payload_consumer/extent_writer.h"
 #include "update_engine/payload_consumer/file_descriptor_utils.h"
+#include "update_engine/payload_consumer/mount_history.h"
 #if USE_MTD
 #include "update_engine/payload_consumer/mtd_file_descriptor.h"
 #endif
@@ -1079,8 +1080,10 @@
 
 // Compare |calculated_hash| with source hash in |operation|, return false and
 // dump hash and set |error| if don't match.
+// |source_fd| is the file descriptor of the source partition.
 bool ValidateSourceHash(const brillo::Blob& calculated_hash,
                         const InstallOperation& operation,
+                        const FileDescriptorPtr source_fd,
                         ErrorCode* error) {
   brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
                                     operation.src_sha256_hash().end());
@@ -1107,6 +1110,9 @@
     LOG(ERROR) << "Operation source (offset:size) in blocks: "
                << base::JoinString(source_extents, ",");
 
+    // Log remount history if this device is an ext4 partition.
+    LogMountHistory(source_fd);
+
     *error = ErrorCode::kDownloadStateInitializationError;
     return false;
   }
@@ -1131,7 +1137,8 @@
                                                      &source_hash));
 
   if (operation.has_src_sha256_hash()) {
-    TEST_AND_RETURN_FALSE(ValidateSourceHash(source_hash, operation, error));
+    TEST_AND_RETURN_FALSE(
+        ValidateSourceHash(source_hash, operation, source_fd_, error));
   }
 
   return true;
@@ -1217,8 +1224,8 @@
     total_blocks -= read_blocks;
   }
   TEST_AND_RETURN_FALSE(source_hasher.Finalize());
-  TEST_AND_RETURN_FALSE(
-      ValidateSourceHash(source_hasher.raw_hash(), operation, error));
+  TEST_AND_RETURN_FALSE(ValidateSourceHash(
+      source_hasher.raw_hash(), operation, source_fd_, error));
   return true;
 }
 
diff --git a/payload_consumer/mount_history.cc b/payload_consumer/mount_history.cc
new file mode 100644
index 0000000..43a75b3
--- /dev/null
+++ b/payload_consumer/mount_history.cc
@@ -0,0 +1,72 @@
+//
+// Copyright (C) 2018 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/payload_consumer/mount_history.h"
+
+#include <inttypes.h>
+
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/time/time.h>
+
+#include "update_engine/common/utils.h"
+
+namespace chromeos_update_engine {
+void LogMountHistory(const FileDescriptorPtr blockdevice_fd) {
+  static constexpr ssize_t kBlockSize = 4096;
+
+  if (blockdevice_fd == nullptr) {
+    return;
+  }
+
+  brillo::Blob block0_buffer(kBlockSize);
+  ssize_t bytes_read;
+
+  if (!utils::PReadAll(
+          blockdevice_fd, block0_buffer.data(), kBlockSize, 0, &bytes_read)) {
+    LOG(WARNING) << "PReadAll failed";
+    return;
+  }
+
+  if (bytes_read != kBlockSize) {
+    LOG(WARNING) << "Could not read an entire block";
+    return;
+  }
+
+  // https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout
+  // Super block starts from block 0, offset 0x400
+  //   0x2C: len32 Mount time
+  //   0x30: len32 Write time
+  //   0x34: len16 Number of mounts since the last fsck
+  //   0x38: len16 Magic signature 0xEF53
+
+  time_t mount_time =
+      *reinterpret_cast<uint32_t*>(&block0_buffer[0x400 + 0x2C]);
+  uint16_t mount_count =
+      *reinterpret_cast<uint16_t*>(&block0_buffer[0x400 + 0x34]);
+  uint16_t magic = *reinterpret_cast<uint16_t*>(&block0_buffer[0x400 + 0x38]);
+
+  if (magic == 0xEF53) {
+    if (mount_count > 0) {
+      LOG(WARNING) << "Device was remounted R/W " << mount_count << " times. "
+                   << "Last remount happened on "
+                   << base::Time::FromTimeT(mount_time) << ".";
+    }
+  }
+}
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/mount_history.h b/payload_consumer/mount_history.h
new file mode 100644
index 0000000..ba0c65d
--- /dev/null
+++ b/payload_consumer/mount_history.h
@@ -0,0 +1,28 @@
+//
+// Copyright (C) 2018 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_PAYLOAD_CONSUMER_MOUNT_HISTORY_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_MOUNT_HISTORY_H_
+
+#include "update_engine/payload_consumer/file_descriptor.h"
+
+namespace chromeos_update_engine {
+// Try to parse an ext4 from the partition specified by |blockdevice_fd|.
+// If ext4 header exists and remount is detected, log mount count and date.
+void LogMountHistory(const FileDescriptorPtr blockdevice_fd);
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_MOUNT_HISTORY_H_
diff --git a/update_engine.gyp b/update_engine.gyp
index f312a1d..f72ca14 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -181,6 +181,7 @@
         'payload_consumer/file_writer.cc',
         'payload_consumer/filesystem_verifier_action.cc',
         'payload_consumer/install_plan.cc',
+        'payload_consumer/mount_history.cc',
         'payload_consumer/payload_constants.cc',
         'payload_consumer/payload_verifier.cc',
         'payload_consumer/postinstall_runner_action.cc',