update_engine: Enterprise channel downgrade

Powerwash and roll back when an enrolled user downgrades the channel:
- If the admin downgrades channel, check for ChannelDowngradebehavior
policy.
- If the user downgrades the channel, powerwash based on given boolean.

Add the "rollback" flag to the powerwash file to try to preserve some
data.

Note that this change is not affecting users yet: The
ChannelDowngradeBehavior policy is not available in DPanel and the UI
does not support chosing powerwash on channel downgrade for enrolled
users.

BUG=chromium:1122531
TEST=FEATURES=test emerge-amd64-generic update_engine
TEST=Set policy with YAPS and test on device

Change-Id: I2f02a6e752eed083b57484766f8e7ecc2eed7aca
Reviewed-on: https://chromium-review.googlesource.com/c/aosp/platform/system/update_engine/+/2397890
Tested-by: Miriam Polzer <mpolzer@google.com>
Commit-Queue: Miriam Polzer <mpolzer@google.com>
Reviewed-by: Amin Hassani <ahassani@chromium.org>
Reviewed-by: Jae Hoon Kim <kimjae@chromium.org>
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 354416e..edcb67b 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -25,6 +25,7 @@
 #include <unordered_set>
 
 #include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
 #include <base/message_loop/message_loop.h>
 #include <brillo/message_loops/base_message_loop.h>
 #include <brillo/message_loops/message_loop.h>
@@ -179,6 +180,7 @@
               bool rollback_allowed,
               bool rollback_data_save_requested,
               int rollback_allowed_milestones,
+              bool rollback_on_channel_downgrade,
               bool obey_proxies,
               bool interactive) override {
     update_called_ = true;
@@ -191,6 +193,7 @@
                               rollback_allowed,
                               rollback_data_save_requested,
                               rollback_allowed_milestones,
+                              rollback_on_channel_downgrade,
                               obey_proxies,
                               interactive);
       return;
@@ -427,7 +430,7 @@
 void UpdateAttempterTest::SessionIdTestChange() {
   EXPECT_NE(UpdateStatus::UPDATED_NEED_REBOOT, attempter_.status());
   const auto old_session_id = attempter_.session_id_;
-  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false, false);
   EXPECT_NE(old_session_id, attempter_.session_id_);
   ScheduleQuitMainLoop();
 }
@@ -798,7 +801,7 @@
     EXPECT_CALL(*processor_, StartProcessing());
   }
 
-  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false, false);
   loop_.PostTask(FROM_HERE,
                  base::Bind(&UpdateAttempterTest::UpdateTestVerify,
                             base::Unretained(this)));
@@ -998,7 +1001,7 @@
   fake_system_state_.set_p2p_manager(&mock_p2p_manager);
   mock_p2p_manager.fake().SetP2PEnabled(false);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(0);
-  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false, false);
   EXPECT_FALSE(actual_using_p2p_for_downloading_);
   EXPECT_FALSE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -1020,7 +1023,7 @@
   mock_p2p_manager.fake().SetEnsureP2PRunningResult(false);
   mock_p2p_manager.fake().SetPerformHousekeepingResult(false);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(0);
-  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false, false);
   EXPECT_FALSE(actual_using_p2p_for_downloading());
   EXPECT_FALSE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -1043,7 +1046,7 @@
   mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
   mock_p2p_manager.fake().SetPerformHousekeepingResult(false);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping());
-  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false, false);
   EXPECT_FALSE(actual_using_p2p_for_downloading());
   EXPECT_FALSE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -1065,7 +1068,7 @@
   mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
   mock_p2p_manager.fake().SetPerformHousekeepingResult(true);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping());
-  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false, false);
   EXPECT_TRUE(actual_using_p2p_for_downloading());
   EXPECT_TRUE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -1097,6 +1100,7 @@
                     false,
                     /*rollback_allowed_milestones=*/0,
                     false,
+                    false,
                     /*interactive=*/true);
   EXPECT_FALSE(actual_using_p2p_for_downloading());
   EXPECT_TRUE(actual_using_p2p_for_sharing());
@@ -1127,7 +1131,7 @@
   attempter_.policy_provider_.reset(
       new policy::PolicyProvider(std::move(device_policy)));
 
-  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false, false);
   EXPECT_EQ(scatter_factor_in_seconds, attempter_.scatter_factor_.InSeconds());
 
   ScheduleQuitMainLoop();
@@ -1165,7 +1169,7 @@
   attempter_.policy_provider_.reset(
       new policy::PolicyProvider(std::move(device_policy)));
 
-  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false, false);
   EXPECT_EQ(scatter_factor_in_seconds, attempter_.scatter_factor_.InSeconds());
 
   // Make sure the file still exists.
@@ -1181,7 +1185,7 @@
   // However, if the count is already 0, it's not decremented. Test that.
   initial_value = 0;
   EXPECT_TRUE(fake_prefs.SetInt64(kPrefsUpdateCheckCount, initial_value));
-  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false, false);
   EXPECT_TRUE(fake_prefs.Exists(kPrefsUpdateCheckCount));
   EXPECT_TRUE(fake_prefs.GetInt64(kPrefsUpdateCheckCount, &new_value));
   EXPECT_EQ(initial_value, new_value);
@@ -1237,6 +1241,7 @@
                     false,
                     /*rollback_allowed_milestones=*/0,
                     false,
+                    false,
                     /*interactive=*/true);
   EXPECT_EQ(scatter_factor_in_seconds, attempter_.scatter_factor_.InSeconds());
 
@@ -1289,7 +1294,7 @@
   FakePrefs fake_prefs;
   SetUpStagingTest(kValidStagingSchedule, &fake_prefs);
 
-  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false, false);
   // Check that prefs have the correct values.
   int64_t update_count;
   EXPECT_TRUE(fake_prefs.GetInt64(kPrefsUpdateCheckCount, &update_count));
@@ -1346,8 +1351,17 @@
   FakePrefs fake_prefs;
   SetUpStagingTest(kValidStagingSchedule, &fake_prefs);
 
-  attempter_.Update(
-      "", "", "", "", "", false, false, 0, false, /* interactive = */ true);
+  attempter_.Update("",
+                    "",
+                    "",
+                    "",
+                    "",
+                    false,
+                    false,
+                    0,
+                    false,
+                    false,
+                    /* interactive = */ true);
   CheckStagingOff();
 
   ScheduleQuitMainLoop();
@@ -1367,8 +1381,17 @@
   FakePrefs fake_prefs;
   SetUpStagingTest(kValidStagingSchedule, &fake_prefs);
 
-  attempter_.Update(
-      "", "", "", "", "", false, false, 0, false, /* interactive = */ true);
+  attempter_.Update("",
+                    "",
+                    "",
+                    "",
+                    "",
+                    false,
+                    false,
+                    0,
+                    false,
+                    false,
+                    /* interactive = */ true);
   CheckStagingOff();
 
   ScheduleQuitMainLoop();
@@ -1697,23 +1720,33 @@
 
 TEST_F(UpdateAttempterTest, TargetVersionPrefixSetAndReset) {
   attempter_.CalculateUpdateParams(
-      "", "", "", "", "1234", false, false, 4, false, false);
+      /*app_version=*/"",
+      /*omaha_url=*/"",
+      /*target_channel=*/"",
+      /*lts_tag=*/"",
+      /*target_version_prefix=*/"1234",
+      /*rollback_allowed=*/false,
+      /*rollback_data_save_requested=*/false,
+      /*rollback_allowed_milestones=*/4,
+      /*rollback_on_channel_downgrade=*/false,
+      /*obey_proxies=*/false,
+      /*interactive=*/false);
   EXPECT_EQ("1234",
             fake_system_state_.request_params()->target_version_prefix());
 
   attempter_.CalculateUpdateParams(
-      "", "", "", "", "", false, 4, false, false, false);
+      "", "", "", "", "", false, false, 4, false, false, false);
   EXPECT_TRUE(
       fake_system_state_.request_params()->target_version_prefix().empty());
 }
 
 TEST_F(UpdateAttempterTest, TargetChannelHintSetAndReset) {
   attempter_.CalculateUpdateParams(
-      "", "", "", "hint", "", false, false, 4, false, false);
+      "", "", "", "hint", "", false, false, 4, false, false, false);
   EXPECT_EQ("hint", fake_system_state_.request_params()->lts_tag());
 
   attempter_.CalculateUpdateParams(
-      "", "", "", "", "", false, 4, false, false, false);
+      "", "", "", "", "", false, false, 4, false, false, false);
   EXPECT_TRUE(fake_system_state_.request_params()->lts_tag().empty());
 }
 
@@ -1726,6 +1759,7 @@
                                    /*rollback_allowed=*/true,
                                    /*rollback_data_save_requested=*/false,
                                    /*rollback_allowed_milestones=*/4,
+                                   /*rollback_on_channel_downgrade=*/false,
                                    false,
                                    false);
   EXPECT_TRUE(fake_system_state_.request_params()->rollback_allowed());
@@ -1740,6 +1774,7 @@
                                    /*rollback_allowed=*/false,
                                    /*rollback_data_save_requested=*/false,
                                    /*rollback_allowed_milestones=*/4,
+                                   /*rollback_on_channel_downgrade=*/false,
                                    false,
                                    false);
   EXPECT_FALSE(fake_system_state_.request_params()->rollback_allowed());
@@ -1747,6 +1782,42 @@
             fake_system_state_.request_params()->rollback_allowed_milestones());
 }
 
+TEST_F(UpdateAttempterTest, ChannelDowngradeNoRollback) {
+  base::ScopedTempDir tempdir;
+  ASSERT_TRUE(tempdir.CreateUniqueTempDir());
+  fake_system_state_.request_params()->set_root(tempdir.GetPath().value());
+  attempter_.CalculateUpdateParams(/*app_version=*/"",
+                                   /*omaha_url=*/"",
+                                   /*target_channel=*/"stable-channel",
+                                   /*lts_tag=*/"",
+                                   /*target_version_prefix=*/"",
+                                   /*rollback_allowed=*/false,
+                                   /*rollback_data_save_requested=*/false,
+                                   /*rollback_allowed_milestones=*/4,
+                                   /*rollback_on_channel_downgrade=*/false,
+                                   /*obey_proxies=*/false,
+                                   /*interactive=*/false);
+  EXPECT_FALSE(fake_system_state_.request_params()->is_powerwash_allowed());
+}
+
+TEST_F(UpdateAttempterTest, ChannelDowngradeRollback) {
+  base::ScopedTempDir tempdir;
+  ASSERT_TRUE(tempdir.CreateUniqueTempDir());
+  fake_system_state_.request_params()->set_root(tempdir.GetPath().value());
+  attempter_.CalculateUpdateParams(/*app_version=*/"",
+                                   /*omaha_url=*/"",
+                                   /*target_channel=*/"stable-channel",
+                                   /*lts_tag=*/"",
+                                   /*target_version_prefix=*/"",
+                                   /*rollback_allowed=*/false,
+                                   /*rollback_data_save_requested=*/false,
+                                   /*rollback_allowed_milestones=*/4,
+                                   /*rollback_on_channel_downgrade=*/true,
+                                   /*obey_proxies=*/false,
+                                   /*interactive=*/false);
+  EXPECT_TRUE(fake_system_state_.request_params()->is_powerwash_allowed());
+}
+
 TEST_F(UpdateAttempterTest, UpdateDeferredByPolicyTest) {
   // Construct an OmahaResponseHandlerAction that has processed an InstallPlan,
   // but the update is being deferred by the Policy.
@@ -1861,7 +1932,7 @@
               SetRollbackHappened(false))
       .Times(expected_reset ? 1 : 0);
   attempter_.policy_provider_ = std::move(mock_policy_provider);
-  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false, false);
   ScheduleQuitMainLoop();
 }
 
@@ -2202,7 +2273,7 @@
         .WillOnce(Return(false));
   attempter_.policy_provider_.reset(
       new policy::PolicyProvider(std::move(device_policy)));
-  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false, false);
 
   EXPECT_EQ(token, attempter_.omaha_request_params_->autoupdate_token());
   ScheduleQuitMainLoop();