update_engine: Deferred update
This change adds in deferred update capability into update_engine.
Ref: http://go/deferredupdates
1. Consumer auto update policy is replaced by deferred update policy
- This is to keep policy more generic + leave the implemenations of the
policy to decide on how to enforce deferred updates.
2. Exposing `ApplyDeferredUpdate()` DBus API
- Client tool will be allowed to call into this method for mainly
testing/verification purposes.
- Chrome will require this API in order actually "apply" the deferred
update, once one is pending, hence is also exposed for `chronos` user
as allowed to invoke.
3. Post installation will now handle hold and apply actions when there
is a deferred update. Please reference changes in the installer and
futility for both FW + OS side changes required to allow for deferred
updates.
BUG=chromium:1278079, b:232304971
TEST=FEATURES=test emerge-$B update_engine update_engine-client
TEST=# autotest related to deferred updates.
Cq-Depend: chromium:3689993
Change-Id: I544a34d466619c5e1de346d34b01c84dec439285
Reviewed-on: https://chromium-review.googlesource.com/c/aosp/platform/system/update_engine/+/3689995
Reviewed-by: Henry Barnor <hbarnor@chromium.org>
Reviewed-by: Yuanpeng Ni <yuanpengni@chromium.org>
Commit-Queue: Jae Hoon Kim <kimjae@chromium.org>
Tested-by: Jae Hoon Kim <kimjae@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 568c923..361ccd2 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -213,7 +213,7 @@
"metrics_utils.cc",
"update_boot_flags_action.cc",
"update_manager/boxed_value.cc",
- "update_manager/consumer_auto_update_policy_impl.cc",
+ "update_manager/deferred_update_policy_impl.cc",
"update_manager/enough_slots_ab_updates_policy_impl.cc",
"update_manager/enterprise_device_policy_impl.cc",
"update_manager/enterprise_rollback_policy_impl.cc",
@@ -541,7 +541,7 @@
"payload_generator/zip_unittest.cc",
"update_boot_flags_action_unittest.cc",
"update_manager/boxed_value_unittest.cc",
- "update_manager/consumer_auto_update_policy_impl_unittest.cc",
+ "update_manager/deferred_update_policy_impl_unittest.cc",
"update_manager/enterprise_device_policy_impl_unittest.cc",
"update_manager/enterprise_rollback_policy_impl_unittest.cc",
"update_manager/evaluation_context_unittest.cc",
diff --git a/UpdateEngine.conf b/UpdateEngine.conf
index ae22ada..0afb4c0 100644
--- a/UpdateEngine.conf
+++ b/UpdateEngine.conf
@@ -26,6 +26,9 @@
send_member="Update"/>
<allow send_destination="org.chromium.UpdateEngine"
send_interface="org.chromium.UpdateEngineInterface"
+ send_member="ApplyDeferredUpdate"/>
+ <allow send_destination="org.chromium.UpdateEngine"
+ send_interface="org.chromium.UpdateEngineInterface"
send_member="AttemptRollback"/>
<allow send_destination="org.chromium.UpdateEngine"
send_interface="org.chromium.UpdateEngineInterface"
diff --git a/client_library/client_dbus.cc b/client_library/client_dbus.cc
index 90d6cc3..01992f8 100644
--- a/client_library/client_dbus.cc
+++ b/client_library/client_dbus.cc
@@ -82,6 +82,10 @@
return proxy_->Update(update_params, nullptr);
}
+bool DBusUpdateEngineClient::ApplyDeferredUpdate() {
+ return proxy_->ApplyDeferredUpdate(nullptr);
+}
+
bool DBusUpdateEngineClient::AttemptInstall(const string& omaha_url,
const vector<string>& dlc_ids) {
return proxy_->AttemptInstall(omaha_url, dlc_ids, nullptr);
diff --git a/client_library/client_dbus.h b/client_library/client_dbus.h
index 0febcd6..48900e8 100644
--- a/client_library/client_dbus.h
+++ b/client_library/client_dbus.h
@@ -42,6 +42,8 @@
bool Update(const update_engine::UpdateParams& update_params) override;
+ bool ApplyDeferredUpdate() override;
+
bool AttemptInstall(const std::string& omaha_url,
const std::vector<std::string>& dlc_ids) override;
diff --git a/client_library/include/update_engine/client.h b/client_library/include/update_engine/client.h
index e8100f4..2ec2113 100644
--- a/client_library/include/update_engine/client.h
+++ b/client_library/include/update_engine/client.h
@@ -40,6 +40,9 @@
// Refer to proto defined in system_api.
virtual bool Update(const update_engine::UpdateParams& update_params) = 0;
+ // Applies the deferred update if there is one.
+ virtual bool ApplyDeferredUpdate() = 0;
+
// Request the update_engine to install a list of DLC modules.
// |omaha_url|
// Force update_engine to look for updates from the given server. Passing
diff --git a/client_library/include/update_engine/update_status.h b/client_library/include/update_engine/update_status.h
index 3e1ed7a..2fd3a15 100644
--- a/client_library/include/update_engine/update_status.h
+++ b/client_library/include/update_engine/update_status.h
@@ -51,8 +51,9 @@
// allow updates, e.g. over cellular network.
NEED_PERMISSION_TO_UPDATE = 10,
CLEANUP_PREVIOUS_UPDATE = 11,
+ UPDATED_BUT_DEFERRED = 12,
- MAX = CLEANUP_PREVIOUS_UPDATE,
+ MAX = UPDATED_BUT_DEFERRED,
// This value is exclusively used in Chrome. DO NOT define nor use it.
// TODO(crbug.com/977320): Remove this value from chrome by refactoring the
diff --git a/common/boot_control_interface.h b/common/boot_control_interface.h
index 67d0284..18b4be6 100644
--- a/common/boot_control_interface.h
+++ b/common/boot_control_interface.h
@@ -55,6 +55,11 @@
// and return kInvalidSlot.
virtual Slot GetCurrentSlot() const = 0;
+ // Return the first slot where we are not running the system from. On success,
+ // the result is a number between 0 and GetNumSlots() - 1, will also not be
+ // equivalent to `GetCurrentSlot()`. Otherwise will return `kInvalidSlot`.
+ virtual Slot GetFirstInactiveSlot() const = 0;
+
// Determines the block device for the given partition name and slot number.
// The |slot| number must be between 0 and GetNumSlots() - 1 and the
// |partition_name| is a platform-specific name that identifies a partition on
diff --git a/common/boot_control_stub.cc b/common/boot_control_stub.cc
index 2e9d0d5..37120a4 100644
--- a/common/boot_control_stub.cc
+++ b/common/boot_control_stub.cc
@@ -35,6 +35,11 @@
return 0;
}
+BootControlInterface::Slot BootControlStub::GetFirstInactiveSlot() const {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return 0;
+}
+
bool BootControlStub::GetPartitionDevice(const std::string& partition_name,
BootControlInterface::Slot slot,
bool not_in_payload,
diff --git a/common/boot_control_stub.h b/common/boot_control_stub.h
index 0007dff..8c9df89 100644
--- a/common/boot_control_stub.h
+++ b/common/boot_control_stub.h
@@ -43,6 +43,7 @@
// BootControlInterface overrides.
unsigned int GetNumSlots() const override;
BootControlInterface::Slot GetCurrentSlot() const override;
+ BootControlInterface::Slot GetFirstInactiveSlot() const override;
bool GetPartitionDevice(const std::string& partition_name,
Slot slot,
bool not_in_payload,
diff --git a/common/constants.cc b/common/constants.cc
index 70ba74f..906da05 100644
--- a/common/constants.cc
+++ b/common/constants.cc
@@ -118,6 +118,7 @@
"wall-clock-staging-wait-period";
const char kPrefsManifestBytes[] = "manifest-bytes";
const char kPrefsConsumerAutoUpdateDisabled[] = "consumer-auto-update-disabled";
+const char kPrefsDeferredUpdateCompleted[] = "deferred-update-completed";
// These four fields are generated by scripts/brillo_update_payload.
const char kPayloadPropertyFileSize[] = "FILE_SIZE";
diff --git a/common/constants.h b/common/constants.h
index cc45fcc..c3dac63 100644
--- a/common/constants.h
+++ b/common/constants.h
@@ -118,6 +118,7 @@
extern const char kPrefsWallClockStagingWaitPeriod[];
extern const char kPrefsManifestBytes[];
extern const char kPrefsConsumerAutoUpdateDisabled[];
+extern const char kPrefsDeferredUpdateCompleted[];
// Keys used when storing and loading payload properties.
extern const char kPayloadPropertyFileSize[];
diff --git a/common/fake_boot_control.h b/common/fake_boot_control.h
index 184d86f..ebfd6e3 100644
--- a/common/fake_boot_control.h
+++ b/common/fake_boot_control.h
@@ -47,6 +47,9 @@
BootControlInterface::Slot GetCurrentSlot() const override {
return current_slot_;
}
+ BootControlInterface::Slot GetFirstInactiveSlot() const override {
+ return first_inactive_slot_;
+ }
bool GetPartitionDevice(const std::string& partition_name,
BootControlInterface::Slot slot,
@@ -110,6 +113,9 @@
}
void SetCurrentSlot(BootControlInterface::Slot slot) { current_slot_ = slot; }
+ void SetFirstInactiveSlot(BootControlInterface::Slot slot) {
+ first_inactive_slot_ = slot;
+ }
void SetPartitionDevice(const std::string& partition_name,
BootControlInterface::Slot slot,
@@ -144,6 +150,7 @@
private:
BootControlInterface::Slot num_slots_{2};
BootControlInterface::Slot current_slot_{0};
+ BootControlInterface::Slot first_inactive_slot_{0};
std::vector<bool> is_bootable_;
std::vector<bool> is_marked_successful_;
diff --git a/common/metrics_constants.h b/common/metrics_constants.h
index 49f0b5a..0261781 100644
--- a/common/metrics_constants.h
+++ b/common/metrics_constants.h
@@ -29,6 +29,7 @@
kDownloadError, // Error downloading response from Omaha.
kParsingError, // Error parsing response.
kRebootPending, // No update check was performed a reboot is pending.
+ kDeferredUpdate, // Update is applied, but deferred.
kNumConstants,
kUnset = -1
diff --git a/cros/boot_control_chromeos.cc b/cros/boot_control_chromeos.cc
index a74e2a5..15ddf6a 100644
--- a/cros/boot_control_chromeos.cc
+++ b/cros/boot_control_chromeos.cc
@@ -191,6 +191,18 @@
return current_slot_;
}
+BootControlInterface::Slot BootControlChromeOS::GetFirstInactiveSlot() const {
+ if (GetCurrentSlot() == BootControlInterface::kInvalidSlot ||
+ GetNumSlots() < 2)
+ return BootControlInterface::kInvalidSlot;
+
+ for (Slot slot = 0; slot < GetNumSlots(); slot++) {
+ if (slot != GetCurrentSlot())
+ return slot;
+ }
+ return BootControlInterface::kInvalidSlot;
+}
+
bool BootControlChromeOS::ParseDlcPartitionName(
const std::string partition_name,
std::string* dlc_id,
diff --git a/cros/boot_control_chromeos.h b/cros/boot_control_chromeos.h
index 3d72463..12c9b3d 100644
--- a/cros/boot_control_chromeos.h
+++ b/cros/boot_control_chromeos.h
@@ -50,6 +50,7 @@
// BootControlInterface overrides.
unsigned int GetNumSlots() const override;
BootControlInterface::Slot GetCurrentSlot() const override;
+ BootControlInterface::Slot GetFirstInactiveSlot() const override;
bool GetPartitionDevice(const std::string& partition_name,
BootControlInterface::Slot slot,
bool not_in_payload,
@@ -73,6 +74,7 @@
private:
friend class BootControlChromeOSTest;
+ FRIEND_TEST(BootControlChromeOSTest, GetFirstInactiveSlot);
FRIEND_TEST(BootControlChromeOSTest, SysfsBlockDeviceTest);
FRIEND_TEST(BootControlChromeOSTest, GetPartitionNumberTest);
FRIEND_TEST(BootControlChromeOSTest, ParseDlcPartitionNameTest);
diff --git a/cros/boot_control_chromeos_unittest.cc b/cros/boot_control_chromeos_unittest.cc
index 80a66f3..6387336 100644
--- a/cros/boot_control_chromeos_unittest.cc
+++ b/cros/boot_control_chromeos_unittest.cc
@@ -35,6 +35,13 @@
BootControlChromeOS bootctl_; // BootControlChromeOS under test.
};
+TEST_F(BootControlChromeOSTest, GetFirstInactiveSlot) {
+ bootctl_.current_slot_ = 0;
+ EXPECT_EQ(1, bootctl_.GetFirstInactiveSlot());
+ bootctl_.current_slot_ = 1;
+ EXPECT_EQ(0, bootctl_.GetFirstInactiveSlot());
+}
+
TEST_F(BootControlChromeOSTest, SysfsBlockDeviceTest) {
EXPECT_EQ("/sys/block/sda", bootctl_.SysfsBlockDevice("/dev/sda"));
EXPECT_EQ("", bootctl_.SysfsBlockDevice("/foo/sda"));
diff --git a/cros/common_service.cc b/cros/common_service.cc
index 4966c63..7247cbe 100644
--- a/cros/common_service.cc
+++ b/cros/common_service.cc
@@ -84,6 +84,14 @@
return true;
}
+bool UpdateEngineService::ApplyDeferredUpdate(ErrorPtr* error) {
+ if (!SystemState::Get()->update_attempter()->ApplyDeferredUpdate()) {
+ LogAndSetError(error, FROM_HERE, "Failed to apply deferred update.");
+ return false;
+ }
+ return true;
+}
+
bool UpdateEngineService::AttemptInstall(brillo::ErrorPtr* error,
const string& omaha_url,
const vector<string>& dlc_ids) {
diff --git a/cros/common_service.h b/cros/common_service.h
index 228ceeb..6705357 100644
--- a/cros/common_service.h
+++ b/cros/common_service.h
@@ -45,6 +45,8 @@
const update_engine::UpdateParams& update_params,
bool* out_result);
+ bool ApplyDeferredUpdate(brillo::ErrorPtr* error);
+
// Attempts a DLC module install operation.
// |omaha_url|: the URL to query for update.
// |dlc_ids|: a list of DLC module IDs.
diff --git a/cros/dbus_service.cc b/cros/dbus_service.cc
index 31baae9..b30d113 100644
--- a/cros/dbus_service.cc
+++ b/cros/dbus_service.cc
@@ -71,6 +71,10 @@
return common_->Update(error, in_update_params, &result);
}
+bool DBusUpdateEngineService::ApplyDeferredUpdate(ErrorPtr* error) {
+ return common_->ApplyDeferredUpdate(error);
+}
+
bool DBusUpdateEngineService::AttemptInstall(ErrorPtr* error,
const string& in_omaha_url,
const vector<string>& dlc_ids) {
diff --git a/cros/dbus_service.h b/cros/dbus_service.h
index b040503..f7a7ffa 100644
--- a/cros/dbus_service.h
+++ b/cros/dbus_service.h
@@ -45,6 +45,8 @@
bool Update(brillo::ErrorPtr* error,
const update_engine::UpdateParams& in_update_params) override;
+ bool ApplyDeferredUpdate(brillo::ErrorPtr* error) override;
+
bool AttemptInstall(brillo::ErrorPtr* error,
const std::string& in_omaha_url,
const std::vector<std::string>& dlc_ids) override;
diff --git a/cros/update_attempter.cc b/cros/update_attempter.cc
index 7eb8d56..215d95f 100644
--- a/cros/update_attempter.cc
+++ b/cros/update_attempter.cc
@@ -158,7 +158,10 @@
// In case of update_engine restart without a reboot we need to restore the
// reboot needed state.
if (GetBootTimeAtUpdate(nullptr)) {
- status_ = UpdateStatus::UPDATED_NEED_REBOOT;
+ if (prefs_->Exists(kPrefsDeferredUpdateCompleted))
+ status_ = UpdateStatus::UPDATED_BUT_DEFERRED;
+ else
+ status_ = UpdateStatus::UPDATED_NEED_REBOOT;
} else {
// Send metric before deleting prefs. Metric tells us how many times the
// inactive partition was updated before the reboot.
@@ -316,6 +319,21 @@
}
LOG(INFO) << "Already updated but checking to see if there are more recent "
"updates available.";
+ } else if (status_ == UpdateStatus::UPDATED_BUT_DEFERRED) {
+ // Update is already deferred, don't proceed with repeated updates.
+ // Although we have applied an update, we still want to ping Omaha
+ // to ensure the number of active statistics is accurate.
+ //
+ // Also convey to the UpdateEngine.Check.Result metric that we're
+ // not performing an update check because of this.
+ LOG(INFO) << "Not updating b/c we deferred update, ping Omaha instead";
+ // TODO(kimjae): Add label for metric.
+ SystemState::Get()->metrics_reporter()->ReportUpdateCheckMetrics(
+ metrics::CheckResult::kDeferredUpdate,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset);
+ PingOmaha();
+ return;
} else if (status_ != UpdateStatus::IDLE) {
// Update in progress. Do nothing.
return;
@@ -1005,6 +1023,53 @@
return true;
}
+bool UpdateAttempter::ApplyDeferredUpdate() {
+ if (status_ != UpdateStatus::UPDATED_BUT_DEFERRED) {
+ LOG(ERROR) << "Cannot apply deferred update when there isn't one "
+ "deferred.";
+ return false;
+ }
+
+ LOG(INFO) << "Applying deferred update.";
+ install_plan_.reset(new InstallPlan());
+ auto* boot_control = SystemState::Get()->boot_control();
+
+ install_plan_->run_post_install = true;
+ install_plan_->defer_update_action = DeferUpdateAction::kApply;
+
+ // Since CrOS is A/B, it's okay to get the first inactive slot.
+ install_plan_->source_slot = boot_control->GetCurrentSlot();
+ install_plan_->target_slot = boot_control->GetFirstInactiveSlot();
+
+ install_plan_->partitions.push_back({
+ .name = "root",
+ .source_size = 1,
+ .target_size = 1,
+ .run_postinstall = true,
+ // TODO(kimjae): Store + override to handle non default script usage.
+ .postinstall_path = kPostinstallDefaultScript,
+ });
+ if (!install_plan_->LoadPartitionsFromSlots(boot_control)) {
+ LOG(ERROR) << "Failed to setup partitions for applying deferred update.";
+ return false;
+ }
+
+ install_plan_->Dump();
+
+ auto install_plan_action =
+ std::make_unique<InstallPlanAction>(*install_plan_);
+ auto postinstall_runner_action = std::make_unique<PostinstallRunnerAction>(
+ boot_control, SystemState::Get()->hardware());
+ postinstall_runner_action->set_delegate(this);
+ BondActions(install_plan_action.get(), postinstall_runner_action.get());
+ processor_->EnqueueAction(std::move(install_plan_action));
+ processor_->EnqueueAction(std::move(postinstall_runner_action));
+ processor_->set_delegate(this);
+
+ ScheduleProcessingStart();
+ return true;
+}
+
bool UpdateAttempter::CheckForInstall(const vector<string>& dlc_ids,
const string& omaha_url) {
if (status_ != UpdateStatus::IDLE) {
@@ -1224,9 +1289,35 @@
if (!SystemState::Get()->dlcservice()->UpdateCompleted(GetSuccessfulDlcIds()))
LOG(WARNING) << "dlcservice didn't successfully handle update completion.";
- SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
- ScheduleUpdates();
- LOG(INFO) << "Update successfully applied, waiting to reboot.";
+
+ if (install_plan_) {
+ switch (install_plan_->defer_update_action) {
+ case DeferUpdateAction::kOff:
+ SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
+ ScheduleUpdates();
+ LOG(INFO) << "Update successfully applied, waiting to reboot.";
+ break;
+ case DeferUpdateAction::kHold:
+ prefs_->SetString(kPrefsDeferredUpdateCompleted, "");
+ SetStatusAndNotify(UpdateStatus::UPDATED_BUT_DEFERRED);
+ ScheduleUpdates();
+ LOG(INFO) << "Deferred update hold action was successful.";
+ return;
+ case DeferUpdateAction::kApply:
+ SetStatusAndNotify(UpdateStatus::UPDATED_BUT_DEFERRED);
+ LOG(INFO) << "Deferred update apply action was successful, "
+ "proceeding with reboot.";
+ if (!ResetStatus()) {
+ LOG(WARNING) << "Failed to reset status.";
+ }
+ RebootIfNeeded();
+ return;
+ }
+ } else {
+ SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
+ ScheduleUpdates();
+ LOG(INFO) << "Update successfully applied, waiting to reboot.";
+ }
// |install_plan_| is null during rollback operations, and the stats don't
// make much sense then anyway.
@@ -1237,6 +1328,7 @@
// Increment pref after every update.
SystemState::Get()->prefs()->SetInt64(kPrefsConsecutiveUpdateCount,
++num_consecutive_updates);
+ // TODO(kimjae): Seperate out apps into categories (OS, DLC, etc).
// Generate an unique payload identifier.
string target_version_uid;
for (const auto& payload : install_plan_->payloads) {
@@ -1400,6 +1492,7 @@
case UpdateStatus::ATTEMPTING_ROLLBACK:
case UpdateStatus::DISABLED:
case UpdateStatus::CLEANUP_PREVIOUS_UPDATE:
+ case UpdateStatus::UPDATED_BUT_DEFERRED:
MarkDeltaUpdateFailure();
// Errored out after partition was marked unbootable.
int64_t num_consecutive_updates = 0;
@@ -1461,7 +1554,11 @@
LOG(INFO)
<< "Cancelling current update but going back to need reboot as there "
"is an update in the inactive partition that can be applied.";
- SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
+ if (prefs_->Exists(kPrefsDeferredUpdateCompleted)) {
+ SetStatusAndNotify(UpdateStatus::UPDATED_BUT_DEFERRED);
+ } else {
+ SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
+ }
return;
}
// One full update never completed or there no longer an inactive partition
@@ -1476,6 +1573,7 @@
ret_value = prefs->Delete(kPrefsUpdateCompletedBootTime) && ret_value;
ret_value = prefs->Delete(kPrefsLastFp, {kDlcPrefsSubDir}) && ret_value;
ret_value = prefs->Delete(kPrefsPreviousVersion) && ret_value;
+ ret_value = prefs->Delete(kPrefsDeferredUpdateCompleted) && ret_value;
return ret_value;
}
@@ -1579,7 +1677,23 @@
LOG(INFO) << "Reset status " << (ret_value ? "successful" : "failed");
return ret_value;
}
+ case UpdateStatus::UPDATED_BUT_DEFERRED: {
+ bool ret_value = true;
+ status_ = UpdateStatus::IDLE;
+ ret_value = ResetUpdatePrefs() && ret_value;
+ // Notify the PayloadState that the successful payload was canceled.
+ SystemState::Get()->payload_state()->ResetUpdateStatus();
+
+ // The previous version is used to report back to omaha after reboot that
+ // we actually rebooted into the new version from this "prev-version". We
+ // need to clear out this value now to prevent it being sent on the next
+ // updatecheck request.
+ ret_value = prefs_->SetString(kPrefsPreviousVersion, "") && ret_value;
+
+ LOG(INFO) << "Reset status " << (ret_value ? "successful" : "failed");
+ return ret_value;
+ }
default:
LOG(ERROR) << "Reset not allowed in this state.";
return false;
@@ -1753,6 +1867,11 @@
/*success=*/false, install_plan_->version);
}
+ if (install_plan_ &&
+ install_plan_->defer_update_action == DeferUpdateAction::kApply) {
+ // TODO(kimjae): Report deferred update apply action failure metric.
+ }
+
// Send it to Omaha.
LOG(INFO) << "Reporting the error event";
auto error_event_action = std::make_unique<OmahaRequestAction>(
@@ -1829,7 +1948,11 @@
UpdateLastCheckedTime();
// Update the status which will schedule the next update check
- SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
+ if (prefs_->Exists(kPrefsDeferredUpdateCompleted)) {
+ SetStatusAndNotify(UpdateStatus::UPDATED_BUT_DEFERRED);
+ } else {
+ SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
+ }
ScheduleUpdates();
}
@@ -1965,11 +2088,14 @@
string boot_id;
TEST_AND_RETURN_FALSE(utils::GetBootId(&boot_id));
+ // Reboots are allowed when updates get deferred, since they are actually
+ // applied just not active. Hence the check on `kPrefsDeferredUpdate`.
string update_completed_on_boot_id;
- if (!prefs_->Exists(kPrefsUpdateCompletedOnBootId) ||
- !prefs_->GetString(kPrefsUpdateCompletedOnBootId,
- &update_completed_on_boot_id) ||
- update_completed_on_boot_id != boot_id)
+ if (!prefs_->Exists(kPrefsDeferredUpdateCompleted) &&
+ (!prefs_->Exists(kPrefsUpdateCompletedOnBootId) ||
+ !prefs_->GetString(kPrefsUpdateCompletedOnBootId,
+ &update_completed_on_boot_id) ||
+ update_completed_on_boot_id != boot_id))
return false;
// Short-circuit avoiding the read in case out_boot_time is nullptr.
diff --git a/cros/update_attempter.h b/cros/update_attempter.h
index 8c8dd3d..815184f 100644
--- a/cros/update_attempter.h
+++ b/cros/update_attempter.h
@@ -142,6 +142,10 @@
// update was already in progress.
virtual bool CheckForUpdate(const update_engine::UpdateParams& update_params);
+ // This is the internal entry point to apply a deferred update, will return
+ // false if there wasn't a deferred update to apply or on failure.
+ virtual bool ApplyDeferredUpdate();
+
// This is the version of CheckForUpdate called by AttemptInstall API.
virtual bool CheckForInstall(const std::vector<std::string>& dlc_ids,
const std::string& omaha_url);
diff --git a/cros/update_attempter_unittest.cc b/cros/update_attempter_unittest.cc
index a82943f..aa5e57f 100644
--- a/cros/update_attempter_unittest.cc
+++ b/cros/update_attempter_unittest.cc
@@ -107,6 +107,7 @@
UpdateStatus::ATTEMPTING_ROLLBACK,
UpdateStatus::DISABLED,
UpdateStatus::NEED_PERMISSION_TO_UPDATE,
+ UpdateStatus::UPDATED_BUT_DEFERRED,
};
struct CheckForUpdateTestParams {
@@ -2704,9 +2705,11 @@
auto* fake_prefs = FakeSystemState::Get()->prefs();
fake_prefs->SetString(kPrefsLastFp, "3.14");
fake_prefs->SetString(kPrefsPreviousVersion, "prev-version");
+ fake_prefs->SetString(kPrefsDeferredUpdateCompleted, "");
// Make sure prefs are deleted.
EXPECT_TRUE(attempter_.ResetUpdatePrefs());
+ EXPECT_FALSE(fake_prefs->Exists(kPrefsDeferredUpdateCompleted));
EXPECT_FALSE(fake_prefs->Exists(kPrefsUpdateCompletedOnBootId));
EXPECT_FALSE(fake_prefs->Exists(kPrefsUpdateCompletedBootTime));
EXPECT_FALSE(fake_prefs->Exists(kPrefsLastFp));
diff --git a/cros/update_engine_client.cc b/cros/update_engine_client.cc
index f3a2dc2..b95d438 100644
--- a/cros/update_engine_client.cc
+++ b/cros/update_engine_client.cc
@@ -226,6 +226,9 @@
"target channel is more stable than the current channel unless "
"--nopowerwash is specified.");
DEFINE_bool(check_for_update, false, "Initiate check for updates.");
+ DEFINE_bool(apply_deferred_update,
+ false,
+ "Apply the deferred update if there is one.");
DEFINE_string(
cohort_hint, "", "Set the current cohort hint to the passed value.");
DEFINE_bool(follow,
@@ -484,6 +487,14 @@
LOG(INFO) << "Target Channel (pending update): " << target_channel;
}
+ if (FLAGS_apply_deferred_update) {
+ if (!client_->ApplyDeferredUpdate()) {
+ LOG(ERROR) << "Apply deferred update failed.";
+ return 1;
+ }
+ return 0;
+ }
+
bool do_update_request = FLAGS_check_for_update || FLAGS_update ||
!FLAGS_app_version.empty() ||
!FLAGS_omaha_url.empty();
diff --git a/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml b/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
index 72f4486..ab5b834 100644
--- a/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
+++ b/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
@@ -29,6 +29,8 @@
value="update_engine::UpdateParams"/>
</arg>
</method>
+ <method name="ApplyDeferredUpdate">
+ </method>
<method name="AttemptInstall">
<arg type="s" name="omaha_url" direction="in" />
<arg type="as" name="dlc_ids" direction="in">
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index 1eea739..abfb03d 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -38,6 +38,12 @@
std::string InstallPayloadTypeToString(InstallPayloadType type);
+enum class DeferUpdateAction {
+ kOff,
+ kHold,
+ kApply,
+};
+
struct InstallPlan {
InstallPlan() = default;
@@ -182,6 +188,9 @@
// Indicates the type of update.
update_engine::UpdateUrgencyInternal update_urgency{
update_engine::UpdateUrgencyInternal::REGULAR};
+
+ // The defer update action to perform during post installation.
+ DeferUpdateAction defer_update_action{DeferUpdateAction::kOff};
};
class InstallPlanAction;
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index 40302e5..1b3c87b 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -95,9 +95,16 @@
}
void PostinstallRunnerAction::PerformPartitionPostinstall() {
- if (install_plan_.download_url.empty()) {
- LOG(INFO) << "Skipping post-install during rollback";
- return CompletePostinstall(ErrorCode::kSuccess);
+ switch (install_plan_.defer_update_action) {
+ case DeferUpdateAction::kOff:
+ if (install_plan_.download_url.empty()) {
+ LOG(INFO) << "Skipping post-install during rollback";
+ return CompletePostinstall(ErrorCode::kSuccess);
+ }
+ break;
+ case DeferUpdateAction::kHold:
+ case DeferUpdateAction::kApply:
+ break;
}
// Skip all the partitions that don't have a post-install step.
@@ -175,6 +182,20 @@
// Chrome OS postinstall expects the target rootfs as the first parameter.
command.push_back(partition.target_path);
+ // Defer update action to apply.
+ switch (install_plan_.defer_update_action) {
+ case DeferUpdateAction::kOff:
+ break;
+ case DeferUpdateAction::kHold:
+ LOG(INFO) << "Defer update action: hold";
+ command.push_back("--defer_update_action=hold");
+ break;
+ case DeferUpdateAction::kApply:
+ LOG(INFO) << "Defer update action: apply";
+ command.push_back("--defer_update_action=apply");
+ break;
+ }
+
current_command_ = Subprocess::Get().ExecFlags(
command,
Subprocess::kRedirectStderrToStdout,
@@ -332,7 +353,15 @@
hardware_->SetWarmReset(true);
}
} else if (install_plan_.run_post_install) {
- error_code = ErrorCode::kUpdatedButNotActive;
+ switch (install_plan_.defer_update_action) {
+ case DeferUpdateAction::kOff:
+ error_code = ErrorCode::kUpdatedButNotActive;
+ break;
+ case DeferUpdateAction::kHold:
+ case DeferUpdateAction::kApply:
+ error_code = ErrorCode::kSuccess;
+ break;
+ }
}
}
diff --git a/update_manager/consumer_auto_update_policy_impl.cc b/update_manager/consumer_auto_update_policy_impl.cc
deleted file mode 100644
index 04c2c48..0000000
--- a/update_manager/consumer_auto_update_policy_impl.cc
+++ /dev/null
@@ -1,73 +0,0 @@
-//
-// Copyright (C) 2021 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/update_manager/consumer_auto_update_policy_impl.h"
-
-#include "update_engine/common/constants.h"
-#include "update_engine/common/system_state.h"
-#include "update_engine/update_manager/update_check_allowed_policy_data.h"
-
-#include <base/logging.h>
-
-namespace chromeos_update_manager {
-
-// Do not perform any updates if consumer has disabled auto updates.
-// However allow interactive updates to continue.
-EvalStatus ConsumerAutoUpdatePolicyImpl::Evaluate(
- EvaluationContext* ec,
- State* state,
- std::string* error,
- PolicyDataInterface* data) const {
- // TODO(crbug.com/1278079): Check for update but skip applying when consumer
- // update is disabled. This will require adding fields to UpdateCheckParams.
- DevicePolicyProvider* const dp_provider = state->device_policy_provider();
- UpdateCheckParams* result =
- UpdateCheckAllowedPolicyData::GetUpdateCheckParams(data);
-
- // Skip check if device is managed.
- const bool* has_owner_p = ec->GetValue(dp_provider->var_has_owner());
- if (has_owner_p && !(*has_owner_p)) {
- LOG(INFO) << "Managed device, ignoring consumer auto update.";
- return EvalStatus::kContinue;
- }
-
- // Otherwise, check if the consumer device has auto updates disabled.
- const bool* updater_consumer_auto_update_disabled_p = ec->GetValue(
- state->updater_provider()->var_consumer_auto_update_disabled());
- if (updater_consumer_auto_update_disabled_p) {
- // Auto update is enabled.
- if (!(*updater_consumer_auto_update_disabled_p)) {
- LOG(INFO) << "Consumer auto update is enabled.";
- return EvalStatus::kContinue;
- }
-
- // Auto update is disabled.
-
- // If interactive, ignore the disabled consumer auto update.
- // This is a safety check.
- if (!result->interactive) {
- LOG(INFO) << "Disabled consumer auto update.";
- return EvalStatus::kAskMeAgainLater;
- }
- LOG(INFO) << "Disabled consumer auto update, "
- << "but continuing as interactive.";
- }
-
- LOG(WARNING) << "Couldn't find consumer auto update value.";
- return EvalStatus::kContinue;
-}
-
-} // namespace chromeos_update_manager
diff --git a/update_manager/consumer_auto_update_policy_impl.h b/update_manager/consumer_auto_update_policy_impl.h
deleted file mode 100644
index 3bc06ac..0000000
--- a/update_manager/consumer_auto_update_policy_impl.h
+++ /dev/null
@@ -1,48 +0,0 @@
-//
-// Copyright (C) 2021 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_UPDATE_MANAGER_CONSUMER_AUTO_UPDATE_POLICY_IMPL_H_
-#define UPDATE_ENGINE_UPDATE_MANAGER_CONSUMER_AUTO_UPDATE_POLICY_IMPL_H_
-
-#include <string>
-
-#include "update_engine/update_manager/policy_interface.h"
-
-namespace chromeos_update_manager {
-
-class ConsumerAutoUpdatePolicyImpl : public PolicyInterface {
- public:
- ConsumerAutoUpdatePolicyImpl() = default;
- ConsumerAutoUpdatePolicyImpl(const ConsumerAutoUpdatePolicyImpl&) = delete;
- ConsumerAutoUpdatePolicyImpl& operator=(const ConsumerAutoUpdatePolicyImpl&) =
- delete;
-
- ~ConsumerAutoUpdatePolicyImpl() override = default;
-
- std::string PolicyName() const override {
- return "ConsumerAutoUpdatePolicyImpl";
- }
-
- // Policy overrides.
- EvalStatus Evaluate(EvaluationContext* ec,
- State* state,
- std::string* error,
- PolicyDataInterface* data) const override;
-};
-
-} // namespace chromeos_update_manager
-
-#endif // UPDATE_ENGINE_UPDATE_MANAGER_CONSUMER_AUTO_UPDATE_POLICY_IMPL_H_
diff --git a/update_manager/consumer_auto_update_policy_impl_unittest.cc b/update_manager/consumer_auto_update_policy_impl_unittest.cc
deleted file mode 100644
index 5fa159e..0000000
--- a/update_manager/consumer_auto_update_policy_impl_unittest.cc
+++ /dev/null
@@ -1,87 +0,0 @@
-//
-// Copyright 2021 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/cros/fake_system_state.h"
-#include "update_engine/update_manager/consumer_auto_update_policy_impl.h"
-#include "update_engine/update_manager/policy_test_utils.h"
-
-using chromeos_update_engine::FakeSystemState;
-using testing::_;
-using testing::Return;
-
-namespace chromeos_update_manager {
-
-class UmConsumerAutoUpdatePolicyImplTest : public UmPolicyTestBase {
- protected:
- UmConsumerAutoUpdatePolicyImplTest() : UmPolicyTestBase() {
- policy_data_.reset(new UpdateCheckAllowedPolicyData());
- policy_2_.reset(new ConsumerAutoUpdatePolicyImpl());
-
- ucp_ =
- UpdateCheckAllowedPolicyData::GetUpdateCheckParams(policy_data_.get());
- }
-
- void SetUp() override {
- UmPolicyTestBase::SetUp();
- FakeSystemState::CreateInstance();
- FakeSystemState::Get()->set_prefs(nullptr);
- }
-
- UpdateCheckParams* ucp_;
-};
-
-TEST_F(UmConsumerAutoUpdatePolicyImplTest, SkipIfDevicePolicyExists) {
- fake_state_.device_policy_provider()->var_device_policy_is_loaded()->reset(
- new bool(true));
- EXPECT_EQ(EvalStatus::kContinue, evaluator_->Evaluate());
-}
-
-TEST_F(UmConsumerAutoUpdatePolicyImplTest, SkipIfNotDisabled) {
- fake_state_.device_policy_provider()->var_has_owner()->reset(new bool(false));
- EXPECT_EQ(EvalStatus::kContinue, evaluator_->Evaluate());
-}
-
-TEST_F(UmConsumerAutoUpdatePolicyImplTest, ConsumerDeviceEnabledAutoUpdate) {
- fake_state_.device_policy_provider()->var_has_owner()->reset(new bool(true));
- fake_state_.updater_provider()->var_consumer_auto_update_disabled()->reset(
- new bool(false));
- EXPECT_EQ(EvalStatus::kContinue, evaluator_->Evaluate());
-}
-
-TEST_F(UmConsumerAutoUpdatePolicyImplTest,
- ConsumerDeviceDisabledAutoUpdateBackgroundCheck) {
- fake_state_.device_policy_provider()->var_has_owner()->reset(new bool(true));
- fake_state_.updater_provider()->var_consumer_auto_update_disabled()->reset(
- new bool(true));
- ucp_->interactive = false;
- EXPECT_EQ(EvalStatus::kAskMeAgainLater, evaluator_->Evaluate());
-}
-
-TEST_F(UmConsumerAutoUpdatePolicyImplTest,
- ConsumerDeviceDisabledAutoUpdateInteractiveCheck) {
- fake_state_.device_policy_provider()->var_has_owner()->reset(new bool(true));
- fake_state_.updater_provider()->var_consumer_auto_update_disabled()->reset(
- new bool(false));
- ucp_->interactive = true;
- EXPECT_EQ(EvalStatus::kContinue, evaluator_->Evaluate());
-}
-
-TEST_F(UmConsumerAutoUpdatePolicyImplTest, ManagedDeviceContinues) {
- fake_state_.device_policy_provider()->var_has_owner()->reset(new bool(false));
- EXPECT_EQ(EvalStatus::kContinue, evaluator_->Evaluate());
-}
-
-} // namespace chromeos_update_manager
diff --git a/update_manager/deferred_update_policy_impl.cc b/update_manager/deferred_update_policy_impl.cc
new file mode 100644
index 0000000..6d962a3
--- /dev/null
+++ b/update_manager/deferred_update_policy_impl.cc
@@ -0,0 +1,74 @@
+//
+// Copyright (C) 2022 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/update_manager/deferred_update_policy_impl.h"
+
+#include "update_engine/common/constants.h"
+#include "update_engine/common/system_state.h"
+#include "update_engine/update_manager/update_can_be_applied_policy_data.h"
+
+#include <base/logging.h>
+
+using chromeos_update_engine::DeferUpdateAction;
+
+namespace chromeos_update_manager {
+
+// Defer updates if consumer has disabled auto updates.
+EvalStatus DeferredUpdatePolicyImpl::Evaluate(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ PolicyDataInterface* data) const {
+ DevicePolicyProvider* const dp_provider = state->device_policy_provider();
+
+ auto* policy_data = static_cast<UpdateCanBeAppliedPolicyData*>(data);
+ auto* install_plan = policy_data->install_plan();
+
+ // Althought the default for `defer_update_action` is `kOff`, explicitly set
+ // in order to not cause potential accidental overrides/defaults missing.
+ install_plan->defer_update_action = DeferUpdateAction::kOff;
+
+ // Skip check if device is managed.
+ const bool* has_owner_p = ec->GetValue(dp_provider->var_has_owner());
+ if (has_owner_p && !(*has_owner_p)) {
+ LOG(INFO) << "Managed device, not deferring updates.";
+ return EvalStatus::kContinue;
+ }
+
+ // Otherwise, check if the consumer device has auto updates disabled.
+ const bool* updater_consumer_auto_update_disabled_p = ec->GetValue(
+ state->updater_provider()->var_consumer_auto_update_disabled());
+ if (updater_consumer_auto_update_disabled_p) {
+ // Consumer auto update is enabled.
+ if (!(*updater_consumer_auto_update_disabled_p)) {
+ LOG(INFO) << "Consumer auto update is enabled, not deferring updates.";
+ return EvalStatus::kContinue;
+ }
+
+ // Consumer auto update is disabled.
+ LOG(INFO) << "Consumer auto update is disabled, deferring updates.";
+ install_plan->defer_update_action = DeferUpdateAction::kHold;
+ // The installer (postinstall) script will hold back the partition table
+ // update, so we must do the same from the autoupdater side.
+ install_plan->switch_slot_on_reboot = false;
+ return EvalStatus::kContinue;
+ }
+
+ LOG(WARNING)
+ << "Couldn't find consumer auto update value, not deferring updates.";
+ return EvalStatus::kContinue;
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/deferred_update_policy_impl.h b/update_manager/deferred_update_policy_impl.h
new file mode 100644
index 0000000..7a90c4d
--- /dev/null
+++ b/update_manager/deferred_update_policy_impl.h
@@ -0,0 +1,45 @@
+//
+// Copyright (C) 2022 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_UPDATE_MANAGER_DEFERRED_UPDATE_POLICY_IMPL_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_DEFERRED_UPDATE_POLICY_IMPL_H_
+
+#include <string>
+
+#include "update_engine/update_manager/policy_interface.h"
+
+namespace chromeos_update_manager {
+
+class DeferredUpdatePolicyImpl : public PolicyInterface {
+ public:
+ DeferredUpdatePolicyImpl() = default;
+ DeferredUpdatePolicyImpl(const DeferredUpdatePolicyImpl&) = delete;
+ DeferredUpdatePolicyImpl& operator=(const DeferredUpdatePolicyImpl&) = delete;
+
+ ~DeferredUpdatePolicyImpl() override = default;
+
+ std::string PolicyName() const override { return "DeferredUpdatePolicyImpl"; }
+
+ // Policy overrides.
+ EvalStatus Evaluate(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ PolicyDataInterface* data) const override;
+};
+
+} // namespace chromeos_update_manager
+
+#endif // UPDATE_ENGINE_UPDATE_MANAGER_DEFERRED_UPDATE_POLICY_IMPL_H_
diff --git a/update_manager/deferred_update_policy_impl_unittest.cc b/update_manager/deferred_update_policy_impl_unittest.cc
new file mode 100644
index 0000000..3f07414
--- /dev/null
+++ b/update_manager/deferred_update_policy_impl_unittest.cc
@@ -0,0 +1,82 @@
+//
+// Copyright 2022 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/cros/fake_system_state.h"
+#include "update_engine/payload_consumer/install_plan.h"
+#include "update_engine/update_manager/deferred_update_policy_impl.h"
+#include "update_engine/update_manager/policy_test_utils.h"
+#include "update_engine/update_manager/update_can_be_applied_policy_data.h"
+
+using chromeos_update_engine::DeferUpdateAction;
+using chromeos_update_engine::FakeSystemState;
+using chromeos_update_engine::InstallPlan;
+using testing::_;
+using testing::Return;
+
+namespace chromeos_update_manager {
+
+class UmDeferredUpdatePolicyImplTest : public UmPolicyTestBase {
+ protected:
+ UmDeferredUpdatePolicyImplTest() : UmPolicyTestBase() {
+ policy_data_.reset(new UpdateCanBeAppliedPolicyData(&install_plan_));
+ policy_2_.reset(new DeferredUpdatePolicyImpl());
+ }
+
+ void SetUp() override {
+ UmPolicyTestBase::SetUp();
+ FakeSystemState::CreateInstance();
+ FakeSystemState::Get()->set_prefs(nullptr);
+ }
+
+ InstallPlan install_plan_;
+};
+
+TEST_F(UmDeferredUpdatePolicyImplTest, SkipIfDevicePolicyExists) {
+ fake_state_.device_policy_provider()->var_device_policy_is_loaded()->reset(
+ new bool(true));
+ EXPECT_EQ(EvalStatus::kContinue, evaluator_->Evaluate());
+ EXPECT_EQ(DeferUpdateAction::kOff, install_plan_.defer_update_action);
+}
+
+TEST_F(UmDeferredUpdatePolicyImplTest, SkipIfNotDisabled) {
+ fake_state_.device_policy_provider()->var_has_owner()->reset(new bool(false));
+ EXPECT_EQ(EvalStatus::kContinue, evaluator_->Evaluate());
+ EXPECT_EQ(DeferUpdateAction::kOff, install_plan_.defer_update_action);
+}
+
+TEST_F(UmDeferredUpdatePolicyImplTest, ConsumerDeviceEnabledAutoUpdate) {
+ fake_state_.device_policy_provider()->var_has_owner()->reset(new bool(true));
+ fake_state_.updater_provider()->var_consumer_auto_update_disabled()->reset(
+ new bool(false));
+ EXPECT_EQ(EvalStatus::kContinue, evaluator_->Evaluate());
+ EXPECT_EQ(DeferUpdateAction::kOff, install_plan_.defer_update_action);
+}
+
+TEST_F(UmDeferredUpdatePolicyImplTest, ConsumerDeviceDisabledAutoUpdate) {
+ fake_state_.device_policy_provider()->var_has_owner()->reset(new bool(true));
+ fake_state_.updater_provider()->var_consumer_auto_update_disabled()->reset(
+ new bool(true));
+ EXPECT_EQ(EvalStatus::kContinue, evaluator_->Evaluate());
+ EXPECT_EQ(DeferUpdateAction::kHold, install_plan_.defer_update_action);
+}
+
+TEST_F(UmDeferredUpdatePolicyImplTest, ManagedDeviceContinues) {
+ fake_state_.device_policy_provider()->var_has_owner()->reset(new bool(false));
+ EXPECT_EQ(EvalStatus::kContinue, evaluator_->Evaluate());
+ EXPECT_EQ(DeferUpdateAction::kOff, install_plan_.defer_update_action);
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/update_can_be_applied_policy.cc b/update_manager/update_can_be_applied_policy.cc
index 89d1fa0..16321be 100644
--- a/update_manager/update_can_be_applied_policy.cc
+++ b/update_manager/update_can_be_applied_policy.cc
@@ -22,6 +22,7 @@
#include <base/logging.h>
#include "update_engine/common/error_code.h"
+#include "update_engine/update_manager/deferred_update_policy_impl.h"
#include "update_engine/update_manager/enterprise_rollback_policy_impl.h"
#include "update_engine/update_manager/interactive_update_policy_impl.h"
#include "update_engine/update_manager/minimum_version_policy_impl.h"
@@ -43,6 +44,7 @@
EnterpriseRollbackPolicyImpl enterprise_rollback_policy;
MinimumVersionPolicyImpl minimum_version_policy;
UpdateTimeRestrictionsPolicyImpl update_time_restrictions_policy;
+ DeferredUpdatePolicyImpl deferred_update_policy;
vector<PolicyInterface const*> policies_to_consult = {
// Check to see if an interactive update has been requested.
@@ -58,6 +60,10 @@
// Do not apply or download an update if we are inside one of the
// restricted times.
&update_time_restrictions_policy,
+
+ // Check to see if deferred updates is required.
+ // Note: Always run later than interactive policy check.
+ &deferred_update_policy,
};
for (auto policy : policies_to_consult) {
diff --git a/update_manager/update_check_allowed_policy.cc b/update_manager/update_check_allowed_policy.cc
index 994e6ba..605343d 100644
--- a/update_manager/update_check_allowed_policy.cc
+++ b/update_manager/update_check_allowed_policy.cc
@@ -21,7 +21,6 @@
#include <base/strings/string_util.h>
#include "update_engine/common/system_state.h"
-#include "update_engine/update_manager/consumer_auto_update_policy_impl.h"
#include "update_engine/update_manager/enough_slots_ab_updates_policy_impl.h"
#include "update_engine/update_manager/enterprise_device_policy_impl.h"
#include "update_engine/update_manager/interactive_update_policy_impl.h"
@@ -69,7 +68,6 @@
OnlyUpdateOfficialBuildsPolicyImpl only_update_official_builds_policy;
InteractiveUpdateCheckAllowedPolicyImpl interactive_update_policy;
OobePolicyImpl oobe_policy;
- ConsumerAutoUpdatePolicyImpl consumer_auto_update_policy;
NextUpdateCheckTimePolicyImpl next_update_check_time_policy;
vector<PolicyInterface* const> policies_to_consult = {
@@ -87,11 +85,6 @@
// Check to see if an interactive update was requested.
&interactive_update_policy,
- // Check to see if consumer auto updates are allowed.
- // Note: Depends on interactive update policy to coninue, so must run
- // after that policy evaluation.
- &consumer_auto_update_policy,
-
// Unofficial builds should not perform periodic update checks.
&only_update_official_builds_policy,
diff --git a/update_status_utils.cc b/update_status_utils.cc
index fdfdd4c..6ed53b3 100644
--- a/update_status_utils.cc
+++ b/update_status_utils.cc
@@ -75,6 +75,8 @@
return update_engine::kUpdateStatusDisabled;
case UpdateStatus::CLEANUP_PREVIOUS_UPDATE:
return update_engine::kUpdateStatusCleanupPreviousUpdate;
+ case UpdateStatus::UPDATED_BUT_DEFERRED:
+ return update_engine::kUpdateStatusUpdatedButDeferred;
}
NOTREACHED();