update_engine: Client limits consecutive updates

For details refer to bug.

BUG=b:201737820
TEST=FEATURES=test emerge-$B update_engine

Change-Id: I8e1816c99539170753f5efaa36b084e24475a06c
Reviewed-on: https://chromium-review.googlesource.com/c/aosp/platform/system/update_engine/+/3335760
Tested-by: Jae Hoon Kim <[email protected]>
Auto-Submit: Jae Hoon Kim <[email protected]>
Commit-Queue: Jae Hoon Kim <[email protected]>
Reviewed-by: Henry Barnor <[email protected]>
Commit-Queue: Henry Barnor <[email protected]>
(cherry picked from commit b43659d5c5dcde58fa1b8b1b696dee8dc6b01f10)
Reviewed-on: https://chromium-review.googlesource.com/c/aosp/platform/system/update_engine/+/3337981
diff --git a/common/constants.h b/common/constants.h
index e655385..fb1a1f1 100644
--- a/common/constants.h
+++ b/common/constants.h
@@ -17,6 +17,8 @@
 #ifndef UPDATE_ENGINE_COMMON_CONSTANTS_H_
 #define UPDATE_ENGINE_COMMON_CONSTANTS_H_
 
+#include <stdint.h>
+
 namespace chromeos_update_engine {
 
 // The root path of all exclusion prefs.
@@ -226,6 +228,9 @@
 // Size in bytes of SHA256 hash.
 const int kSHA256Size = 32;
 
+// Limit on the number of consecutive updates.
+const int64_t kConsecutiveUpdateLimit = 3;
+
 }  // namespace chromeos_update_engine
 
 #endif  // UPDATE_ENGINE_COMMON_CONSTANTS_H_
diff --git a/cros/mock_update_attempter.h b/cros/mock_update_attempter.h
index 8eb0c1f..2146194 100644
--- a/cros/mock_update_attempter.h
+++ b/cros/mock_update_attempter.h
@@ -63,6 +63,8 @@
   MOCK_CONST_METHOD0(consecutive_failed_update_checks, unsigned int(void));
 
   MOCK_CONST_METHOD0(server_dictated_poll_interval, unsigned int(void));
+
+  MOCK_METHOD0(IsRepeatedUpdatesEnabled, bool(void));
 };
 
 }  // namespace chromeos_update_engine
diff --git a/cros/omaha_request_action_unittest.cc b/cros/omaha_request_action_unittest.cc
index 3f7105d..8512cdd 100644
--- a/cros/omaha_request_action_unittest.cc
+++ b/cros/omaha_request_action_unittest.cc
@@ -446,6 +446,9 @@
         .expected_download_error_code = metrics::DownloadErrorCode::kUnset,
     };
 
+    ON_CALL(*FakeSystemState::Get()->mock_update_attempter(),
+            IsRepeatedUpdatesEnabled())
+        .WillByDefault(Return(true));
     ON_CALL(*FakeSystemState::Get()->mock_update_attempter(), GetExcluder())
         .WillByDefault(Return(&mock_excluder_));
   }
diff --git a/cros/omaha_request_builder_xml_unittest.cc b/cros/omaha_request_builder_xml_unittest.cc
index 7e6b273..ae79abd 100644
--- a/cros/omaha_request_builder_xml_unittest.cc
+++ b/cros/omaha_request_builder_xml_unittest.cc
@@ -66,6 +66,10 @@
     FakeSystemState::CreateInstance();
     params_.set_hw_details(false);
     FakeSystemState::Get()->set_request_params(&params_);
+
+    ON_CALL(*FakeSystemState::Get()->mock_update_attempter(),
+            IsRepeatedUpdatesEnabled())
+        .WillByDefault(Return(true));
   }
   void TearDown() override {}
 
diff --git a/cros/update_attempter.cc b/cros/update_attempter.cc
index 3f227aa..35c1315 100644
--- a/cros/update_attempter.cc
+++ b/cros/update_attempter.cc
@@ -2013,12 +2013,25 @@
 }
 
 bool UpdateAttempter::IsRepeatedUpdatesEnabled() {
+  auto* prefs = SystemState::Get()->prefs();
+
+  // Limit the number of repeated updates allowed as a safeguard on client.
+  // Whether consecutive update feature is allowed or not.
+  // Refer to b/201737820.
+  int64_t consecutive_updates = 0;
+  prefs->GetInt64(kPrefsConsecutiveUpdateCount, &consecutive_updates);
+  if (consecutive_updates >= kConsecutiveUpdateLimit) {
+    LOG(WARNING) << "Not allowing repeated updates as limit reached.";
+    return false;
+  }
+
   bool allow_repeated_updates;
   if (!SystemState::Get()->prefs()->GetBoolean(kPrefsAllowRepeatedUpdates,
                                                &allow_repeated_updates)) {
     // Defaulting to true.
-    allow_repeated_updates = true;
+    return true;
   }
+
   return allow_repeated_updates;
 }
 
diff --git a/cros/update_attempter.h b/cros/update_attempter.h
index eedfda7..ab9b9db 100644
--- a/cros/update_attempter.h
+++ b/cros/update_attempter.h
@@ -245,7 +245,7 @@
 
   // Returns whether repeated updates are enabled. Defaults to true if the pref
   // is unset or unable to be read.
-  bool IsRepeatedUpdatesEnabled();
+  virtual bool IsRepeatedUpdatesEnabled();
 
   // |DaemonStateInterface| overrides.
   bool StartUpdater() override;
@@ -327,6 +327,7 @@
   FRIEND_TEST(UpdateAttempterTest, FirstUpdateBeforeReboot);
   FRIEND_TEST(UpdateAttempterTest, InvalidateLastUpdate);
   FRIEND_TEST(UpdateAttempterTest, ConsecutiveUpdateBeforeRebootSuccess);
+  FRIEND_TEST(UpdateAttempterTest, ConsecutiveUpdateBeforeRebootLimited);
   FRIEND_TEST(UpdateAttempterTest, ConsecutiveUpdateFailureMetric);
   FRIEND_TEST(UpdateAttempterTest, ResetUpdatePrefs);
   FRIEND_TEST(UpdateAttempterTest, ProcessingDoneSkipApplying);
diff --git a/cros/update_attempter_unittest.cc b/cros/update_attempter_unittest.cc
index bc0ac88..cf7499c 100644
--- a/cros/update_attempter_unittest.cc
+++ b/cros/update_attempter_unittest.cc
@@ -266,6 +266,11 @@
         .WillRepeatedly(ReturnPointee(&actual_using_p2p_for_sharing_));
   }
 
+  void TearDown() override {
+    prefs_->Delete(kPrefsAllowRepeatedUpdates);
+    prefs_->Delete(kPrefsConsecutiveUpdateCount);
+  }
+
  public:
   void ScheduleQuitMainLoop();
 
@@ -2464,15 +2469,39 @@
 
   // Consecutive update metric should be updated.
   int64_t num_updates;
-  FakeSystemState::Get()->prefs()->GetInt64(kPrefsConsecutiveUpdateCount,
-                                            &num_updates);
+  prefs_->GetInt64(kPrefsConsecutiveUpdateCount, &num_updates);
   EXPECT_EQ(num_updates, 1);
+
+  // Validate that consecutive count gets incremented on updates.
+  TestProcessingDone();
+  prefs_->GetInt64(kPrefsConsecutiveUpdateCount, &num_updates);
+  EXPECT_EQ(num_updates, 2);
+}
+
+TEST_F(UpdateAttempterTest, ConsecutiveUpdateBeforeRebootLimited) {
+  // Default should be true.
+  EXPECT_TRUE(attempter_.IsRepeatedUpdatesEnabled());
+
+  // Disabled feature.
+  EXPECT_TRUE(prefs_->SetBoolean(kPrefsAllowRepeatedUpdates, false));
+  EXPECT_FALSE(attempter_.IsRepeatedUpdatesEnabled());
+
+  EXPECT_TRUE(prefs_->SetBoolean(kPrefsAllowRepeatedUpdates, true));
+  EXPECT_TRUE(attempter_.IsRepeatedUpdatesEnabled());
+
+  EXPECT_TRUE(prefs_->SetInt64(kPrefsConsecutiveUpdateCount,
+                               kConsecutiveUpdateLimit - 1));
+  EXPECT_TRUE(attempter_.IsRepeatedUpdatesEnabled());
+
+  EXPECT_TRUE(
+      prefs_->SetInt64(kPrefsConsecutiveUpdateCount, kConsecutiveUpdateLimit));
+  EXPECT_FALSE(attempter_.IsRepeatedUpdatesEnabled());
 }
 
 TEST_F(UpdateAttempterTest, ConsecutiveUpdateFailureMetric) {
   MockAction action;
   // Two previous consecutive updates.
-  FakeSystemState::Get()->prefs()->SetInt64(kPrefsConsecutiveUpdateCount, 2);
+  prefs_->SetInt64(kPrefsConsecutiveUpdateCount, 2);
 
   // Fail an update.
   EXPECT_CALL(action, Type()).WillRepeatedly(Return("MockAction"));