PM: Add an update engine provider.
The UpdaterProvider exports variables for querying the status of an
update process and related settings. Includes a concrete implementation
(RealUpdaterProvider), which currently links directly with update engine
code and pulls information through the SystemState object. Also
included is a fake implementation (FakeUpdaterProvider) for testing
purposes.
BUG=chromium:346914
TEST=Unit tests.
Change-Id: I6ed5b40f21e43537e78aebf4217d811e149f745b
Reviewed-on: https://chromium-review.googlesource.com/192232
Reviewed-by: Gilad Arnold <garnold@chromium.org>
Commit-Queue: Gilad Arnold <garnold@chromium.org>
Tested-by: Gilad Arnold <garnold@chromium.org>
diff --git a/policy_manager/real_updater_provider.cc b/policy_manager/real_updater_provider.cc
new file mode 100644
index 0000000..dbfae87
--- /dev/null
+++ b/policy_manager/real_updater_provider.cc
@@ -0,0 +1,338 @@
+// Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/policy_manager/real_updater_provider.h"
+
+#include <inttypes.h>
+
+#include <string>
+
+#include <base/time/time.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/dbus/service_constants.h>
+
+#include "update_engine/clock_interface.h"
+#include "update_engine/omaha_request_params.h"
+#include "update_engine/prefs.h"
+#include "update_engine/update_attempter.h"
+
+using base::StringPrintf;
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_engine::OmahaRequestParams;
+using chromeos_update_engine::SystemState;
+using std::string;
+
+namespace chromeos_policy_manager {
+
+// A templated base class for all update related variables. Provides uniform
+// construction and a system state handle.
+template<typename T>
+class UpdaterVariableBase : public Variable<T> {
+ public:
+ UpdaterVariableBase(const string& name, SystemState* system_state)
+ : Variable<T>(name, kVariableModePoll), system_state_(system_state) {}
+
+ protected:
+ // The system state used for pulling information from the updater.
+ inline SystemState* system_state() const { return system_state_; }
+
+ private:
+ SystemState* const system_state_;
+};
+
+// Helper class for issuing a GetStatus() to the UpdateAttempter.
+class GetStatusHelper {
+ public:
+ GetStatusHelper(SystemState* system_state, string* errmsg) {
+ is_success_ = system_state->update_attempter()->GetStatus(
+ &last_checked_time_, &progress_, &update_status_, &new_version_,
+ &payload_size_);
+ if (!is_success_ && errmsg)
+ *errmsg = "Failed to get a status update from the update engine";
+ }
+
+ inline bool is_success() { return is_success_; }
+ inline int64_t last_checked_time() { return last_checked_time_; }
+ inline double progress() { return progress_; }
+ inline const string& update_status() { return update_status_; }
+ inline const string& new_version() { return new_version_; }
+ inline int64_t payload_size() { return payload_size_; }
+
+ private:
+ bool is_success_;
+ int64_t last_checked_time_;
+ double progress_;
+ string update_status_;
+ string new_version_;
+ int64_t payload_size_;
+};
+
+// A variable reporting the time when a last update check was issued.
+class LastCheckedTimeVariable : public UpdaterVariableBase<Time> {
+ public:
+ using UpdaterVariableBase<Time>::UpdaterVariableBase;
+
+ private:
+ virtual const Time* GetValue(TimeDelta /* timeout */,
+ string* errmsg) override {
+ GetStatusHelper raw(system_state(), errmsg);
+ if (!raw.is_success())
+ return NULL;
+
+ return new Time(Time::FromTimeT(raw.last_checked_time()));
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(LastCheckedTimeVariable);
+};
+
+// A variable reporting the update (download) progress as a decimal fraction
+// between 0.0 and 1.0.
+class ProgressVariable : public UpdaterVariableBase<double> {
+ public:
+ using UpdaterVariableBase<double>::UpdaterVariableBase;
+
+ private:
+ virtual const double* GetValue(TimeDelta /* timeout */,
+ string* errmsg) override {
+ GetStatusHelper raw(system_state(), errmsg);
+ if (!raw.is_success())
+ return NULL;
+
+ if (raw.progress() < 0.0 || raw.progress() > 1.0) {
+ if (errmsg) {
+ *errmsg = StringPrintf("Invalid progress value received: %f",
+ raw.progress());
+ }
+ return NULL;
+ }
+
+ return new double(raw.progress());
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ProgressVariable);
+};
+
+// A variable reporting the stage in which the update process is.
+class StageVariable : public UpdaterVariableBase<Stage> {
+ public:
+ using UpdaterVariableBase<Stage>::UpdaterVariableBase;
+
+ private:
+ struct CurrOpStrToStage {
+ const char* str;
+ Stage stage;
+ };
+ static const CurrOpStrToStage curr_op_str_to_stage[];
+
+ // Note: the method is defined outside the class so arraysize can work.
+ virtual const Stage* GetValue(TimeDelta /* timeout */,
+ string* errmsg) override;
+
+ DISALLOW_COPY_AND_ASSIGN(StageVariable);
+};
+
+const StageVariable::CurrOpStrToStage StageVariable::curr_op_str_to_stage[] = {
+ {update_engine::kUpdateStatusIdle, Stage::kIdle},
+ {update_engine::kUpdateStatusCheckingForUpdate, Stage::kCheckingForUpdate},
+ {update_engine::kUpdateStatusUpdateAvailable, Stage::kUpdateAvailable},
+ {update_engine::kUpdateStatusDownloading, Stage::kDownloading},
+ {update_engine::kUpdateStatusVerifying, Stage::kVerifying},
+ {update_engine::kUpdateStatusFinalizing, Stage::kFinalizing},
+ {update_engine::kUpdateStatusUpdatedNeedReboot, Stage::kUpdatedNeedReboot},
+ {update_engine::kUpdateStatusReportingErrorEvent,
+ Stage::kReportingErrorEvent},
+ {update_engine::kUpdateStatusAttemptingRollback, Stage::kAttemptingRollback},
+};
+
+const Stage* StageVariable::GetValue(TimeDelta /* timeout */,
+ string* errmsg) {
+ GetStatusHelper raw(system_state(), errmsg);
+ if (!raw.is_success())
+ return NULL;
+
+ for (auto& key_val : curr_op_str_to_stage)
+ if (raw.update_status() == key_val.str)
+ return new Stage(key_val.stage);
+
+ if (errmsg)
+ *errmsg = string("Unknown update status: ") + raw.update_status();
+ return NULL;
+}
+
+// A variable reporting the version number that an update is updating to.
+class NewVersionVariable : public UpdaterVariableBase<string> {
+ public:
+ using UpdaterVariableBase<string>::UpdaterVariableBase;
+
+ private:
+ virtual const string* GetValue(TimeDelta /* timeout */,
+ string* errmsg) override {
+ GetStatusHelper raw(system_state(), errmsg);
+ if (!raw.is_success())
+ return NULL;
+
+ return new string(raw.new_version());
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(NewVersionVariable);
+};
+
+// A variable reporting the size of the update being processed in bytes.
+class PayloadSizeVariable : public UpdaterVariableBase<size_t> {
+ public:
+ using UpdaterVariableBase<size_t>::UpdaterVariableBase;
+
+ private:
+ virtual const size_t* GetValue(TimeDelta /* timeout */,
+ string* errmsg) override {
+ GetStatusHelper raw(system_state(), errmsg);
+ if (!raw.is_success())
+ return NULL;
+
+ if (raw.payload_size() < 0) {
+ if (errmsg)
+ *errmsg = string("Invalid payload size: %" PRId64, raw.payload_size());
+ return NULL;
+ }
+
+ return new size_t(static_cast<size_t>(raw.payload_size()));
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(PayloadSizeVariable);
+};
+
+// A variable reporting the point in time an update last completed in the
+// current boot cycle.
+//
+// TODO(garnold) In general, both the current boottime and wallclock time
+// readings should come from the time provider and be moderated by the
+// evaluation context, so that they are uniform throughout the evaluation of a
+// policy request.
+class UpdateCompletedTimeVariable : public UpdaterVariableBase<Time> {
+ public:
+ using UpdaterVariableBase<Time>::UpdaterVariableBase;
+
+ private:
+ virtual const Time* GetValue(TimeDelta /* timeout */,
+ string* errmsg) override {
+ Time update_boottime;
+ if (!system_state()->update_attempter()->GetBootTimeAtUpdate(
+ &update_boottime)) {
+ if (errmsg)
+ *errmsg = "Update completed time could not be read";
+ return NULL;
+ }
+
+ chromeos_update_engine::ClockInterface* clock = system_state()->clock();
+ Time curr_boottime = clock->GetBootTime();
+ if (curr_boottime < update_boottime) {
+ if (errmsg)
+ *errmsg = "Update completed time more recent than current time";
+ return NULL;
+ }
+ TimeDelta duration_since_update = curr_boottime - update_boottime;
+ return new Time(clock->GetWallclockTime() - duration_since_update);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(UpdateCompletedTimeVariable);
+};
+
+// Variables reporting the current image channel.
+class CurrChannelVariable : public UpdaterVariableBase<string> {
+ public:
+ using UpdaterVariableBase<string>::UpdaterVariableBase;
+
+ private:
+ virtual const string* GetValue(TimeDelta /* timeout */,
+ string* errmsg) override {
+ OmahaRequestParams* request_params = system_state()->request_params();
+ string channel = request_params->current_channel();
+ if (channel.empty()) {
+ if (errmsg)
+ *errmsg = "No current channel";
+ return NULL;
+ }
+ return new string(channel);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(CurrChannelVariable);
+};
+
+// Variables reporting the new image channel.
+class NewChannelVariable : public UpdaterVariableBase<string> {
+ public:
+ using UpdaterVariableBase<string>::UpdaterVariableBase;
+
+ private:
+ virtual const string* GetValue(TimeDelta /* timeout */,
+ string* errmsg) override {
+ OmahaRequestParams* request_params = system_state()->request_params();
+ string channel = request_params->target_channel();
+ if (channel.empty()) {
+ if (errmsg)
+ *errmsg = "No new channel";
+ return NULL;
+ }
+ return new string(channel);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(NewChannelVariable);
+};
+
+// A variable class for reading Boolean prefs values.
+class BooleanPrefVariable : public UpdaterVariableBase<bool> {
+ public:
+ BooleanPrefVariable(const string& name, SystemState* system_state,
+ const char* key, bool default_val)
+ : UpdaterVariableBase<bool>(name, system_state),
+ key_(key), default_val_(default_val) {}
+
+ private:
+ virtual const bool* GetValue(TimeDelta /* timeout */,
+ string* errmsg) override {
+ bool result = default_val_;
+ chromeos_update_engine::PrefsInterface* prefs = system_state()->prefs();
+ if (prefs && prefs->Exists(key_) && !prefs->GetBoolean(key_, &result)) {
+ if (errmsg)
+ *errmsg = string("Could not read boolean pref ") + key_;
+ return NULL;
+ }
+ return new bool(result);
+ }
+
+ // The Boolean preference key and default value.
+ const char* const key_;
+ const bool default_val_;
+
+ DISALLOW_COPY_AND_ASSIGN(BooleanPrefVariable);
+};
+
+
+// RealUpdaterProvider methods.
+
+RealUpdaterProvider::RealUpdaterProvider(SystemState* system_state)
+ : system_state_(system_state),
+ var_last_checked_time_(
+ new LastCheckedTimeVariable("last_checked_time", system_state_)),
+ var_update_completed_time_(
+ new UpdateCompletedTimeVariable("update_completed_time",
+ system_state_)),
+ var_progress_(new ProgressVariable("progress", system_state_)),
+ var_stage_(new StageVariable("stage", system_state_)),
+ var_new_version_(new NewVersionVariable("new_version", system_state_)),
+ var_payload_size_(new PayloadSizeVariable("payload_size", system_state_)),
+ var_curr_channel_(new CurrChannelVariable("curr_channel", system_state_)),
+ var_new_channel_(new NewChannelVariable("new_channel", system_state_)),
+ var_p2p_enabled_(
+ new BooleanPrefVariable("p2p_enabled", system_state_,
+ chromeos_update_engine::kPrefsP2PEnabled,
+ false)),
+ var_cellular_enabled_(
+ new BooleanPrefVariable(
+ "cellular_enabled", system_state_,
+ chromeos_update_engine::kPrefsUpdateOverCellularPermission,
+ false)) {}
+
+} // namespace chromeos_policy_manager