update_engine: Place enterprise rollback save marker file

- There are now two types of rollback. One that does a full powerwash
  and one that save some system state.
- When an enterprise rollback powerwash is scheduled, place a marker
  file to tell the shutdown process to save data before rebooting.
- This lets rollback preserve additional data over a powerwash that
  can be restored later.
- Change a few places that were using is_rollback to save_rollback_data
  to be explicit.

BUG=chromium:955463
TEST=unittests

Change-Id: I9f18319e711e425a6e712dd319e03bcc6ddd0a1b
Reviewed-on: https://chromium-review.googlesource.com/1414030
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: Zentaro Kavanagh <zentaro@chromium.org>
Reviewed-by: Amin Hassani <ahassani@chromium.org>
diff --git a/common/fake_hardware.h b/common/fake_hardware.h
index 3e5a66e..53b2dd5 100644
--- a/common/fake_hardware.h
+++ b/common/fake_hardware.h
@@ -104,15 +104,15 @@
 
   int GetPowerwashCount() const override { return powerwash_count_; }
 
-  bool SchedulePowerwash(bool is_rollback) override {
+  bool SchedulePowerwash(bool save_rollback_data) override {
     powerwash_scheduled_ = true;
-    is_rollback_powerwash_ = is_rollback;
+    save_rollback_data_ = save_rollback_data;
     return true;
   }
 
   bool CancelPowerwash() override {
     powerwash_scheduled_ = false;
-    is_rollback_powerwash_ = false;
+    save_rollback_data_ = false;
     return true;
   }
 
@@ -193,7 +193,7 @@
   int GetMaxKernelKeyRollforward() const { return kernel_max_rollforward_; }
 
   bool GetIsRollbackPowerwashScheduled() const {
-    return powerwash_scheduled_ && is_rollback_powerwash_;
+    return powerwash_scheduled_ && save_rollback_data_;
   }
 
  private:
@@ -213,7 +213,7 @@
   int firmware_max_rollforward_{kFirmwareMaxRollforward};
   int powerwash_count_{kPowerwashCountNotSet};
   bool powerwash_scheduled_{false};
-  bool is_rollback_powerwash_{false};
+  bool save_rollback_data_{false};
   int64_t build_timestamp_{0};
   bool first_active_omaha_ping_sent_{false};
 
diff --git a/common/hardware_interface.h b/common/hardware_interface.h
index 0140588..6c53540 100644
--- a/common/hardware_interface.h
+++ b/common/hardware_interface.h
@@ -102,9 +102,9 @@
   virtual int GetPowerwashCount() const = 0;
 
   // Signals that a powerwash (stateful partition wipe) should be performed
-  // after reboot. If |is_rollback| is true additional state is preserved
-  // during shutdown that can be restored after the powerwash.
-  virtual bool SchedulePowerwash(bool is_rollback) = 0;
+  // after reboot. If |save_rollback_data| is true additional state is
+  // preserved during shutdown that can be restored after the powerwash.
+  virtual bool SchedulePowerwash(bool save_rollback_data) = 0;
 
   // Cancel the powerwash operation scheduled to be performed on next boot.
   virtual bool CancelPowerwash() = 0;
diff --git a/hardware_android.cc b/hardware_android.cc
index 21d4659..80c7757 100644
--- a/hardware_android.cc
+++ b/hardware_android.cc
@@ -152,9 +152,10 @@
   return 0;
 }
 
-bool HardwareAndroid::SchedulePowerwash(bool is_rollback) {
+bool HardwareAndroid::SchedulePowerwash(bool save_rollback_data) {
   LOG(INFO) << "Scheduling a powerwash to BCB.";
-  LOG_IF(WARNING, is_rollback) << "is_rollback was true but isn't supported.";
+  LOG_IF(WARNING, save_rollback_data) << "save_rollback_data was true but "
+                                      << "isn't supported.";
   string err;
   if (!update_bootloader_message({"--wipe_data", "--reason=wipe_data_from_ota"},
                                  &err)) {
diff --git a/hardware_android.h b/hardware_android.h
index 5b3c99d..c59a152 100644
--- a/hardware_android.h
+++ b/hardware_android.h
@@ -48,7 +48,7 @@
   bool SetMaxFirmwareKeyRollforward(int firmware_max_rollforward) override;
   bool SetMaxKernelKeyRollforward(int kernel_max_rollforward) override;
   int GetPowerwashCount() const override;
-  bool SchedulePowerwash(bool is_rollback) override;
+  bool SchedulePowerwash(bool save_rollback_data) override;
   bool CancelPowerwash() override;
   bool GetNonVolatileDirectory(base::FilePath* path) const override;
   bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
diff --git a/hardware_chromeos.cc b/hardware_chromeos.cc
index 8ef05b2..60583e1 100644
--- a/hardware_chromeos.cc
+++ b/hardware_chromeos.cc
@@ -61,6 +61,11 @@
 const char kPowerwashMarkerFile[] =
     "/mnt/stateful_partition/factory_install_reset";
 
+// The name of the marker file used to trigger a save of rollback data
+// during the next shutdown.
+const char kRollbackSaveMarkerFile[] =
+    "/mnt/stateful_partition/.save_rollback_data";
+
 // The contents of the powerwash marker file for the non-rollback case.
 const char kPowerwashCommand[] = "safe fast keepimg reason=update_engine\n";
 
@@ -226,15 +231,25 @@
   return powerwash_count;
 }
 
-bool HardwareChromeOS::SchedulePowerwash(bool is_rollback) {
+bool HardwareChromeOS::SchedulePowerwash(bool save_rollback_data) {
+  if (save_rollback_data) {
+    if (!utils::WriteFile(kRollbackSaveMarkerFile, nullptr, 0)) {
+      PLOG(ERROR) << "Error in creating rollback save marker file: "
+                  << kRollbackSaveMarkerFile << ". Rollback will not"
+                  << " preserve any data.";
+    } else {
+      LOG(INFO) << "Rollback data save has been scheduled on next shutdown.";
+    }
+  }
+
   const char* powerwash_command =
-      is_rollback ? kRollbackPowerwashCommand : kPowerwashCommand;
+      save_rollback_data ? kRollbackPowerwashCommand : kPowerwashCommand;
   bool result = utils::WriteFile(
       kPowerwashMarkerFile, powerwash_command, strlen(powerwash_command));
   if (result) {
     LOG(INFO) << "Created " << kPowerwashMarkerFile
-              << " to powerwash on next reboot (is_rollback=" << is_rollback
-              << ")";
+              << " to powerwash on next reboot ("
+              << "save_rollback_data=" << save_rollback_data << ")";
   } else {
     PLOG(ERROR) << "Error in creating powerwash marker file: "
                 << kPowerwashMarkerFile;
@@ -254,6 +269,11 @@
                 << kPowerwashMarkerFile;
   }
 
+  // Delete the rollback save marker file if it existed.
+  if (!base::DeleteFile(base::FilePath(kRollbackSaveMarkerFile), false)) {
+    PLOG(ERROR) << "Could not remove rollback save marker";
+  }
+
   return result;
 }
 
diff --git a/hardware_chromeos.h b/hardware_chromeos.h
index 8829866..04bdae3 100644
--- a/hardware_chromeos.h
+++ b/hardware_chromeos.h
@@ -53,7 +53,7 @@
   bool SetMaxFirmwareKeyRollforward(int firmware_max_rollforward) override;
   bool SetMaxKernelKeyRollforward(int kernel_max_rollforward) override;
   int GetPowerwashCount() const override;
-  bool SchedulePowerwash(bool is_rollback) override;
+  bool SchedulePowerwash(bool save_rollback_data) override;
   bool CancelPowerwash() override;
   bool GetNonVolatileDirectory(base::FilePath* path) const override;
   bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
diff --git a/mock_update_attempter.h b/mock_update_attempter.h
index d97163d..c39fb62 100644
--- a/mock_update_attempter.h
+++ b/mock_update_attempter.h
@@ -30,12 +30,13 @@
  public:
   using UpdateAttempter::UpdateAttempter;
 
-  MOCK_METHOD8(Update,
+  MOCK_METHOD9(Update,
                void(const std::string& app_version,
                     const std::string& omaha_url,
                     const std::string& target_channel,
                     const std::string& target_version_prefix,
                     bool rollback_allowed,
+                    bool rollback_data_save_requested,
                     int rollback_allowed_milestones,
                     bool obey_proxies,
                     bool interactive));
diff --git a/omaha_request_params.h b/omaha_request_params.h
index 6691bee..2d2ab69 100644
--- a/omaha_request_params.h
+++ b/omaha_request_params.h
@@ -51,6 +51,7 @@
         delta_okay_(true),
         interactive_(false),
         rollback_allowed_(false),
+        rollback_data_save_requested_(false),
         wall_clock_based_wait_enabled_(false),
         update_check_count_wait_enabled_(false),
         min_update_checks_needed_(kDefaultMinUpdateChecks),
@@ -132,6 +133,15 @@
 
   inline bool rollback_allowed() const { return rollback_allowed_; }
 
+  inline void set_rollback_data_save_requested(
+      bool rollback_data_save_requested) {
+    rollback_data_save_requested_ = rollback_data_save_requested;
+  }
+
+  inline bool rollback_data_save_requested() const {
+    return rollback_data_save_requested_;
+  }
+
   inline void set_rollback_allowed_milestones(int rollback_allowed_milestones) {
     rollback_allowed_milestones_ = rollback_allowed_milestones;
   }
@@ -330,6 +340,9 @@
   // Whether the client is accepting rollback images too.
   bool rollback_allowed_;
 
+  // Whether rollbacks should preserve some system state during powerwash.
+  bool rollback_data_save_requested_;
+
   // How many milestones the client can rollback to.
   int rollback_allowed_milestones_;
 
diff --git a/omaha_response_handler_action.cc b/omaha_response_handler_action.cc
index d05bc46..5741a2b 100644
--- a/omaha_response_handler_action.cc
+++ b/omaha_response_handler_action.cc
@@ -178,6 +178,8 @@
       return;
     }
     install_plan_.is_rollback = true;
+    install_plan_.rollback_data_save_requested =
+        params->rollback_data_save_requested();
   }
 
   if (response.powerwash_required || params->ShouldPowerwash())
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index ede36b3..17cefd8 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -146,6 +146,9 @@
   // True if this update is a rollback.
   bool is_rollback{false};
 
+  // True if this rollback should preserve some system data.
+  bool rollback_data_save_requested{false};
+
   // True if the update should write verity.
   // False otherwise.
   bool write_verity{true};
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index a782b8f..cc3843d 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -57,9 +57,14 @@
   CHECK(HasInputObject());
   install_plan_ = GetInputObject();
 
-  // Currently we're always powerwashing when rolling back.
+  // We always powerwash when rolling back, however policy can determine
+  // if this is a full/normal powerwash, or a special rollback powerwash
+  // that retains a small amount of system state such as enrollment and
+  // network configuration. In both cases all user accounts are deleted.
   if (install_plan_.powerwash_required || install_plan_.is_rollback) {
-    if (hardware_->SchedulePowerwash(install_plan_.is_rollback)) {
+    bool save_rollback_data =
+        install_plan_.is_rollback && install_plan_.rollback_data_save_requested;
+    if (hardware_->SchedulePowerwash(save_rollback_data)) {
       powerwash_scheduled_ = true;
     } else {
       return CompletePostinstall(ErrorCode::kPostinstallPowerwashError);
diff --git a/payload_consumer/postinstall_runner_action_unittest.cc b/payload_consumer/postinstall_runner_action_unittest.cc
index caee5e2..04c81fa 100644
--- a/payload_consumer/postinstall_runner_action_unittest.cc
+++ b/payload_consumer/postinstall_runner_action_unittest.cc
@@ -100,7 +100,8 @@
   void RunPostinstallAction(const string& device_path,
                             const string& postinstall_program,
                             bool powerwash_required,
-                            bool is_rollback);
+                            bool is_rollback,
+                            bool save_rollback_data);
 
  public:
   void ResumeRunningAction() {
@@ -170,7 +171,8 @@
     const string& device_path,
     const string& postinstall_program,
     bool powerwash_required,
-    bool is_rollback) {
+    bool is_rollback,
+    bool save_rollback_data) {
   ActionProcessor processor;
   processor_ = &processor;
   auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
@@ -184,6 +186,7 @@
   install_plan.download_url = "http://127.0.0.1:8080/update";
   install_plan.powerwash_required = powerwash_required;
   install_plan.is_rollback = is_rollback;
+  install_plan.rollback_data_save_requested = save_rollback_data;
   feeder_action->set_obj(install_plan);
   auto runner_action = std::make_unique<PostinstallRunnerAction>(
       &fake_boot_control_, &fake_hardware_);
@@ -249,7 +252,8 @@
 TEST_F(PostinstallRunnerActionTest, RunAsRootSimpleTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
 
-  RunPostinstallAction(loop.dev(), kPostinstallDefaultScript, false, false);
+  RunPostinstallAction(
+      loop.dev(), kPostinstallDefaultScript, false, false, false);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
   EXPECT_TRUE(processor_delegate_.processing_done_called_);
 
@@ -260,7 +264,7 @@
 
 TEST_F(PostinstallRunnerActionTest, RunAsRootRunSymlinkFileTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
-  RunPostinstallAction(loop.dev(), "bin/postinst_link", false, false);
+  RunPostinstallAction(loop.dev(), "bin/postinst_link", false, false, false);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
 }
 
@@ -270,6 +274,7 @@
   RunPostinstallAction(loop.dev(),
                        "bin/postinst_example",
                        /*powerwash_required=*/true,
+                       false,
                        false);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
 
@@ -278,14 +283,31 @@
   EXPECT_FALSE(fake_hardware_.GetIsRollbackPowerwashScheduled());
 }
 
-TEST_F(PostinstallRunnerActionTest, RunAsRootRollbackTest) {
+TEST_F(PostinstallRunnerActionTest, RunAsRootRollbackTestNoDataSave) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
 
   // Run a simple postinstall program, rollback happened.
   RunPostinstallAction(loop.dev(),
                        "bin/postinst_example",
                        false,
-                       /*is_rollback=*/true);
+                       /*is_rollback=*/true,
+                       /*save_rollback_data=*/false);
+  EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
+
+  // Check that powerwash was scheduled and that it's NOT a rollback powerwash.
+  EXPECT_TRUE(fake_hardware_.IsPowerwashScheduled());
+  EXPECT_FALSE(fake_hardware_.GetIsRollbackPowerwashScheduled());
+}
+
+TEST_F(PostinstallRunnerActionTest, RunAsRootRollbackTestWithDataSave) {
+  ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
+
+  // Run a simple postinstall program, rollback happened.
+  RunPostinstallAction(loop.dev(),
+                       "bin/postinst_example",
+                       false,
+                       /*is_rollback=*/true,
+                       /*save_rollback_data=*/true);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
 
   // Check that powerwash was scheduled and that it's a rollback powerwash.
@@ -296,7 +318,8 @@
 // Runs postinstall from a partition file that doesn't mount, so it should
 // fail.
 TEST_F(PostinstallRunnerActionTest, RunAsRootCantMountTest) {
-  RunPostinstallAction("/dev/null", kPostinstallDefaultScript, false, false);
+  RunPostinstallAction(
+      "/dev/null", kPostinstallDefaultScript, false, false, false);
   EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_);
 
   // In case of failure, Postinstall should not signal a powerwash even if it
@@ -309,7 +332,7 @@
 // fail.
 TEST_F(PostinstallRunnerActionTest, RunAsRootErrScriptTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
-  RunPostinstallAction(loop.dev(), "bin/postinst_fail1", false, false);
+  RunPostinstallAction(loop.dev(), "bin/postinst_fail1", false, false, false);
   EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_);
 }
 
@@ -317,7 +340,7 @@
 // UMA with a different error code. Test those cases are properly detected.
 TEST_F(PostinstallRunnerActionTest, RunAsRootFirmwareBErrScriptTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
-  RunPostinstallAction(loop.dev(), "bin/postinst_fail3", false, false);
+  RunPostinstallAction(loop.dev(), "bin/postinst_fail3", false, false, false);
   EXPECT_EQ(ErrorCode::kPostinstallBootedFromFirmwareB,
             processor_delegate_.code_);
 }
@@ -325,7 +348,7 @@
 // Check that you can't specify an absolute path.
 TEST_F(PostinstallRunnerActionTest, RunAsRootAbsolutePathNotAllowedTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
-  RunPostinstallAction(loop.dev(), "/etc/../bin/sh", false, false);
+  RunPostinstallAction(loop.dev(), "/etc/../bin/sh", false, false, false);
   EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_);
 }
 
@@ -334,7 +357,8 @@
 // SElinux labels are only set on Android.
 TEST_F(PostinstallRunnerActionTest, RunAsRootCheckFileContextsTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
-  RunPostinstallAction(loop.dev(), "bin/self_check_context", false, false);
+  RunPostinstallAction(
+      loop.dev(), "bin/self_check_context", false, false, false);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
 }
 #endif  // __ANDROID__
@@ -347,7 +371,7 @@
   loop_.PostTask(FROM_HERE,
                  base::Bind(&PostinstallRunnerActionTest::SuspendRunningAction,
                             base::Unretained(this)));
-  RunPostinstallAction(loop.dev(), "bin/postinst_suspend", false, false);
+  RunPostinstallAction(loop.dev(), "bin/postinst_suspend", false, false, false);
   // postinst_suspend returns 0 only if it was suspended at some point.
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
   EXPECT_TRUE(processor_delegate_.processing_done_called_);
@@ -359,7 +383,7 @@
 
   // Wait for the action to start and then cancel it.
   CancelWhenStarted();
-  RunPostinstallAction(loop.dev(), "bin/postinst_suspend", false, false);
+  RunPostinstallAction(loop.dev(), "bin/postinst_suspend", false, false, false);
   // When canceling the action, the action never finished and therefore we had
   // a ProcessingStopped call instead.
   EXPECT_FALSE(processor_delegate_.code_set_);
@@ -382,7 +406,8 @@
 
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
   setup_action_delegate_ = &mock_delegate_;
-  RunPostinstallAction(loop.dev(), "bin/postinst_progress", false, false);
+  RunPostinstallAction(
+      loop.dev(), "bin/postinst_progress", false, false, false);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
 }
 
diff --git a/update_attempter.cc b/update_attempter.cc
index 31a6ce4..44eea77 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -239,6 +239,7 @@
                              const string& target_channel,
                              const string& target_version_prefix,
                              bool rollback_allowed,
+                             bool rollback_data_save_requested,
                              int rollback_allowed_milestones,
                              bool obey_proxies,
                              bool interactive) {
@@ -275,6 +276,7 @@
                              target_channel,
                              target_version_prefix,
                              rollback_allowed,
+                             rollback_data_save_requested,
                              rollback_allowed_milestones,
                              obey_proxies,
                              interactive)) {
@@ -349,6 +351,7 @@
                                             const string& target_channel,
                                             const string& target_version_prefix,
                                             bool rollback_allowed,
+                                            bool rollback_data_save_requested,
                                             int rollback_allowed_milestones,
                                             bool obey_proxies,
                                             bool interactive) {
@@ -368,6 +371,10 @@
   // Set whether rollback is allowed.
   omaha_request_params_->set_rollback_allowed(rollback_allowed);
 
+  // Set whether saving data over rollback is requested.
+  omaha_request_params_->set_rollback_data_save_requested(
+      rollback_data_save_requested);
+
   CalculateStagingParams(interactive);
   // If staging_wait_time_ wasn't set, staging is off, use scattering instead.
   if (staging_wait_time_.InSeconds() == 0) {
@@ -953,6 +960,7 @@
            params.target_channel,
            params.target_version_prefix,
            params.rollback_allowed,
+           params.rollback_data_save_requested,
            params.rollback_allowed_milestones,
            /*obey_proxies=*/false,
            params.interactive);
diff --git a/update_attempter.h b/update_attempter.h
index 6c25eb2..82b81ce 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -85,6 +85,7 @@
                       const std::string& target_channel,
                       const std::string& target_version_prefix,
                       bool rollback_allowed,
+                      bool rollback_data_save_requested,
                       int rollback_allowed_milestones,
                       bool obey_proxies,
                       bool interactive);
@@ -341,6 +342,7 @@
                              const std::string& target_channel,
                              const std::string& target_version_prefix,
                              bool rollback_allowed,
+                             bool rollback_data_save_requested,
                              int rollback_allowed_milestones,
                              bool obey_proxies,
                              bool interactive);
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index ec6066b..14fad38 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -506,7 +506,7 @@
     EXPECT_CALL(*processor_, StartProcessing());
   }
 
-  attempter_.Update("", "", "", "", false, 0, false, false);
+  attempter_.Update("", "", "", "", false, false, 0, false, false);
   loop_.PostTask(FROM_HERE,
                  base::Bind(&UpdateAttempterTest::UpdateTestVerify,
                             base::Unretained(this)));
@@ -706,7 +706,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, 0, false, false);
+  attempter_.Update("", "", "", "", false, false, 0, false, false);
   EXPECT_FALSE(actual_using_p2p_for_downloading_);
   EXPECT_FALSE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -728,7 +728,7 @@
   mock_p2p_manager.fake().SetEnsureP2PRunningResult(false);
   mock_p2p_manager.fake().SetPerformHousekeepingResult(false);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(0);
-  attempter_.Update("", "", "", "", false, 0, false, false);
+  attempter_.Update("", "", "", "", false, false, 0, false, false);
   EXPECT_FALSE(actual_using_p2p_for_downloading());
   EXPECT_FALSE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -751,7 +751,7 @@
   mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
   mock_p2p_manager.fake().SetPerformHousekeepingResult(false);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping());
-  attempter_.Update("", "", "", "", false, 0, false, false);
+  attempter_.Update("", "", "", "", false, false, 0, false, false);
   EXPECT_FALSE(actual_using_p2p_for_downloading());
   EXPECT_FALSE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -773,7 +773,7 @@
   mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
   mock_p2p_manager.fake().SetPerformHousekeepingResult(true);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping());
-  attempter_.Update("", "", "", "", false, 0, false, false);
+  attempter_.Update("", "", "", "", false, false, 0, false, false);
   EXPECT_TRUE(actual_using_p2p_for_downloading());
   EXPECT_TRUE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -801,6 +801,7 @@
                     "",
                     "",
                     false,
+                    false,
                     /*rollback_allowed_milestones=*/0,
                     false,
                     /*interactive=*/true);
@@ -833,7 +834,7 @@
   attempter_.policy_provider_.reset(
       new policy::PolicyProvider(std::move(device_policy)));
 
-  attempter_.Update("", "", "", "", false, 0, false, false);
+  attempter_.Update("", "", "", "", false, false, 0, false, false);
   EXPECT_EQ(scatter_factor_in_seconds, attempter_.scatter_factor_.InSeconds());
 
   ScheduleQuitMainLoop();
@@ -871,7 +872,7 @@
   attempter_.policy_provider_.reset(
       new policy::PolicyProvider(std::move(device_policy)));
 
-  attempter_.Update("", "", "", "", false, 0, false, false);
+  attempter_.Update("", "", "", "", false, false, 0, false, false);
   EXPECT_EQ(scatter_factor_in_seconds, attempter_.scatter_factor_.InSeconds());
 
   // Make sure the file still exists.
@@ -887,7 +888,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, 0, false, false);
+  attempter_.Update("", "", "", "", false, false, 0, false, false);
   EXPECT_TRUE(fake_prefs.Exists(kPrefsUpdateCheckCount));
   EXPECT_TRUE(fake_prefs.GetInt64(kPrefsUpdateCheckCount, &new_value));
   EXPECT_EQ(initial_value, new_value);
@@ -939,6 +940,7 @@
                     "",
                     "",
                     false,
+                    false,
                     /*rollback_allowed_milestones=*/0,
                     false,
                     /*interactive=*/true);
@@ -993,7 +995,7 @@
   FakePrefs fake_prefs;
   SetUpStagingTest(kValidStagingSchedule, &fake_prefs);
 
-  attempter_.Update("", "", "", "", false, 0, false, false);
+  attempter_.Update("", "", "", "", false, false, 0, false, false);
   // Check that prefs have the correct values.
   int64_t update_count;
   EXPECT_TRUE(fake_prefs.GetInt64(kPrefsUpdateCheckCount, &update_count));
@@ -1050,7 +1052,8 @@
   FakePrefs fake_prefs;
   SetUpStagingTest(kValidStagingSchedule, &fake_prefs);
 
-  attempter_.Update("", "", "", "", false, 0, false, /* interactive = */ true);
+  attempter_.Update(
+      "", "", "", "", false, false, 0, false, /* interactive = */ true);
   CheckStagingOff();
 
   ScheduleQuitMainLoop();
@@ -1070,7 +1073,8 @@
   FakePrefs fake_prefs;
   SetUpStagingTest(kValidStagingSchedule, &fake_prefs);
 
-  attempter_.Update("", "", "", "", false, 0, false, /* interactive = */ true);
+  attempter_.Update(
+      "", "", "", "", false, false, 0, false, /* interactive = */ true);
   CheckStagingOff();
 
   ScheduleQuitMainLoop();
@@ -1240,11 +1244,13 @@
 }
 
 TEST_F(UpdateAttempterTest, TargetVersionPrefixSetAndReset) {
-  attempter_.CalculateUpdateParams("", "", "", "1234", false, 4, false, false);
+  attempter_.CalculateUpdateParams(
+      "", "", "", "1234", false, false, 4, false, false);
   EXPECT_EQ("1234",
             fake_system_state_.request_params()->target_version_prefix());
 
-  attempter_.CalculateUpdateParams("", "", "", "", false, 4, false, false);
+  attempter_.CalculateUpdateParams(
+      "", "", "", "", false, 4, false, false, false);
   EXPECT_TRUE(
       fake_system_state_.request_params()->target_version_prefix().empty());
 }
@@ -1255,6 +1261,7 @@
                                    "",
                                    "1234",
                                    /*rollback_allowed=*/true,
+                                   /*rollback_data_save_requested=*/false,
                                    /*rollback_allowed_milestones=*/4,
                                    false,
                                    false);
@@ -1267,6 +1274,7 @@
                                    "",
                                    "1234",
                                    /*rollback_allowed=*/false,
+                                   /*rollback_data_save_requested=*/false,
                                    /*rollback_allowed_milestones=*/4,
                                    false,
                                    false);
@@ -1392,7 +1400,7 @@
               SetRollbackHappened(false))
       .Times(expected_reset ? 1 : 0);
   attempter_.policy_provider_ = std::move(mock_policy_provider);
-  attempter_.Update("", "", "", "", false, 0, false, false);
+  attempter_.Update("", "", "", "", false, false, 0, false, false);
   ScheduleQuitMainLoop();
 }
 
diff --git a/update_manager/android_things_policy.cc b/update_manager/android_things_policy.cc
index 4afcf12..26bd0ba 100644
--- a/update_manager/android_things_policy.cc
+++ b/update_manager/android_things_policy.cc
@@ -54,6 +54,7 @@
   result->target_channel.clear();
   result->target_version_prefix.clear();
   result->rollback_allowed = false;
+  result->rollback_data_save_requested = false;
   result->rollback_allowed_milestones = -1;
   result->interactive = false;
 
diff --git a/update_manager/enterprise_device_policy_impl.cc b/update_manager/enterprise_device_policy_impl.cc
index a3430ef..3d77d59 100644
--- a/update_manager/enterprise_device_policy_impl.cc
+++ b/update_manager/enterprise_device_policy_impl.cc
@@ -91,22 +91,27 @@
         case RollbackToTargetVersion::kDisabled:
           LOG(INFO) << "Policy disables rollbacks.";
           result->rollback_allowed = false;
+          result->rollback_data_save_requested = false;
           break;
         case RollbackToTargetVersion::kRollbackAndPowerwash:
           LOG(INFO) << "Policy allows rollbacks with powerwash.";
           result->rollback_allowed = true;
+          result->rollback_data_save_requested = false;
           break;
         case RollbackToTargetVersion::kRollbackAndRestoreIfPossible:
           LOG(INFO)
               << "Policy allows rollbacks, also tries to restore if possible.";
-          // We don't support restore yet, but policy still allows rollback.
           result->rollback_allowed = true;
+          result->rollback_data_save_requested = true;
           break;
         case RollbackToTargetVersion::kRollbackOnlyIfRestorePossible:
+          // TODO(crbug.com/947621): Remove this policy option until we know
+          // how it could be supported correctly.
           LOG(INFO) << "Policy only allows rollbacks if restore is possible.";
           // We don't support restore yet, policy doesn't allow rollback in this
           // case.
           result->rollback_allowed = false;
+          result->rollback_data_save_requested = false;
           break;
         case RollbackToTargetVersion::kMaxValue:
           NOTREACHED();
diff --git a/update_manager/policy.h b/update_manager/policy.h
index 5d65d9a..9e7df10 100644
--- a/update_manager/policy.h
+++ b/update_manager/policy.h
@@ -50,6 +50,8 @@
   std::string target_version_prefix;
   // Specifies whether rollback images are allowed by device policy.
   bool rollback_allowed;
+  // Specifies if rollbacks should attempt to preserve some system state.
+  bool rollback_data_save_requested;
   // Specifies the number of Chrome milestones rollback should be allowed,
   // starting from the stable version at any time. Value is -1 if unspecified
   // (e.g. no device policy is available yet), in this case no version