Rename the PolicyManager to UpdateManager.
This change renames the PolicyManager class, directory, tests, etc,
to avoid confusion with libpolicy and its classes.
BUG=chromium:373551
TEST=emerged on link.
CQ-DEPEND=CL:I43081673c7ba409f02273197da7915537bde39c6
Change-Id: Iffa76caa3b95ecbbdba87ab01006d1d8ce35a27f
Reviewed-on: https://chromium-review.googlesource.com/201876
Tested-by: Alex Deymo <deymo@chromium.org>
Reviewed-by: David Zeuthen <zeuthen@chromium.org>
Commit-Queue: Alex Deymo <deymo@chromium.org>
diff --git a/update_manager/boxed_value.cc b/update_manager/boxed_value.cc
new file mode 100644
index 0000000..512f7a8
--- /dev/null
+++ b/update_manager/boxed_value.cc
@@ -0,0 +1,163 @@
+// 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 <set>
+#include <string>
+
+#include <base/strings/string_number_conversions.h>
+#include <base/time/time.h>
+
+#include "update_engine/update_manager/boxed_value.h"
+#include "update_engine/update_manager/shill_provider.h"
+#include "update_engine/update_manager/updater_provider.h"
+#include "update_engine/utils.h"
+
+using std::set;
+using std::string;
+
+namespace chromeos_update_manager {
+
+// Template instantiation for common types; used in BoxedValue::ToString().
+// Keep in sync with boxed_value_unitttest.cc.
+
+template<>
+string BoxedValue::ValuePrinter<string>(const void *value) {
+ const string* val = reinterpret_cast<const string*>(value);
+ return *val;
+}
+
+template<>
+string BoxedValue::ValuePrinter<int>(const void *value) {
+ const int* val = reinterpret_cast<const int*>(value);
+ return base::IntToString(*val);
+}
+
+template<>
+string BoxedValue::ValuePrinter<unsigned int>(const void *value) {
+ const unsigned int* val = reinterpret_cast<const unsigned int*>(value);
+ return base::UintToString(*val);
+}
+
+template<>
+string BoxedValue::ValuePrinter<int64_t>(const void *value) {
+ const int64_t* val = reinterpret_cast<const int64_t*>(value);
+ return base::Int64ToString(*val);
+}
+
+template<>
+string BoxedValue::ValuePrinter<uint64_t>(const void *value) {
+ const uint64_t* val =
+ reinterpret_cast<const uint64_t*>(value);
+ return base::Uint64ToString(static_cast<uint64>(*val));
+}
+
+template<>
+string BoxedValue::ValuePrinter<bool>(const void *value) {
+ const bool* val = reinterpret_cast<const bool*>(value);
+ return *val ? "true" : "false";
+}
+
+template<>
+string BoxedValue::ValuePrinter<double>(const void *value) {
+ const double* val = reinterpret_cast<const double*>(value);
+ return base::DoubleToString(*val);
+}
+
+template<>
+string BoxedValue::ValuePrinter<base::Time>(const void *value) {
+ const base::Time* val = reinterpret_cast<const base::Time*>(value);
+ return chromeos_update_engine::utils::ToString(*val);
+}
+
+template<>
+string BoxedValue::ValuePrinter<base::TimeDelta>(const void *value) {
+ const base::TimeDelta* val = reinterpret_cast<const base::TimeDelta*>(value);
+ return chromeos_update_engine::utils::FormatTimeDelta(*val);
+}
+
+static std::string ConnectionTypeToString(ConnectionType type) {
+ switch (type) {
+ case ConnectionType::kEthernet:
+ return "Ethernet";
+ case ConnectionType::kWifi:
+ return "Wifi";
+ case ConnectionType::kWimax:
+ return "Wimax";
+ case ConnectionType::kBluetooth:
+ return "Bluetooth";
+ case ConnectionType::kCellular:
+ return "Cellular";
+ case ConnectionType::kUnknown:
+ return "Unknown";
+ }
+ NOTREACHED();
+ return "Unknown";
+}
+
+template<>
+string BoxedValue::ValuePrinter<ConnectionType>(const void *value) {
+ const ConnectionType* val = reinterpret_cast<const ConnectionType*>(value);
+ return ConnectionTypeToString(*val);
+}
+
+template<>
+string BoxedValue::ValuePrinter<set<ConnectionType>>(const void *value) {
+ string ret = "";
+ const set<ConnectionType>* val =
+ reinterpret_cast<const set<ConnectionType>*>(value);
+ for (auto& it : *val) {
+ ConnectionType type = it;
+ if (ret.size() > 0)
+ ret += ",";
+ ret += ConnectionTypeToString(type);
+ }
+ return ret;
+}
+
+template<>
+string BoxedValue::ValuePrinter<ConnectionTethering>(const void *value) {
+ const ConnectionTethering* val =
+ reinterpret_cast<const ConnectionTethering*>(value);
+ switch (*val) {
+ case ConnectionTethering::kNotDetected:
+ return "Not Detected";
+ case ConnectionTethering::kSuspected:
+ return "Suspected";
+ case ConnectionTethering::kConfirmed:
+ return "Confirmed";
+ case ConnectionTethering::kUnknown:
+ return "Unknown";
+ }
+ NOTREACHED();
+ return "Unknown";
+}
+
+template<>
+string BoxedValue::ValuePrinter<Stage>(const void *value) {
+ const Stage* val = reinterpret_cast<const Stage*>(value);
+ switch (*val) {
+ case Stage::kIdle:
+ return "Idle";
+ case Stage::kCheckingForUpdate:
+ return "Checking For Update";
+ case Stage::kUpdateAvailable:
+ return "Update Available";
+ case Stage::kDownloading:
+ return "Downloading";
+ case Stage::kVerifying:
+ return "Verifying";
+ case Stage::kFinalizing:
+ return "Finalizing";
+ case Stage::kUpdatedNeedReboot:
+ return "Updated, Need Reboot";
+ case Stage::kReportingErrorEvent:
+ return "Reporting Error Event";
+ case Stage::kAttemptingRollback:
+ return "Attempting Rollback";
+ }
+ NOTREACHED();
+ return "Unknown";
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/boxed_value.h b/update_manager/boxed_value.h
new file mode 100644
index 0000000..69ad0a4
--- /dev/null
+++ b/update_manager/boxed_value.h
@@ -0,0 +1,111 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_BOXED_VALUE_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_BOXED_VALUE_H_
+
+#include <string>
+
+#include <base/basictypes.h>
+
+namespace chromeos_update_manager {
+
+// BoxedValue is a class to hold pointers of a given type that deletes them when
+// the instance goes out of scope, as scoped_ptr<T> does. The main difference
+// with it is that the type T is not part of the class, i.e., this isn't a
+// parametric class. The class has a parametric contructor that accepts a
+// const T* which will define the type of the object passed on delete.
+//
+// It is safe to use this class in linked containers such as std::list and
+// std::map but the object can't be copied. This means that you need to
+// construct the BoxedValue inplace using a container method like emplace()
+// or move it with std::move().
+//
+// list<BoxedValue> lst;
+// lst.emplace_back(new const int(42));
+// lst.emplace_back(new const string("Hello world!"));
+//
+// map<int, BoxedValue> m;
+// m.emplace(123, std::move(BoxedValue(new const string("Hola mundo!"))));
+//
+// auto it = m.find(42);
+// if (it != m.end())
+// cout << "m[42] points to " << it->second.value() << endl;
+// cout << "m[33] points to " << m[33].value() << endl;
+//
+// Since copy and assign are not allowed, you can't create a copy of the
+// BoxedValue which means that you can only use a reference to it.
+//
+
+class BoxedValue {
+ public:
+ // Creates an empty BoxedValue. Since the pointer can't be assigned from other
+ // BoxedValues or pointers, this is only useful in places where a default
+ // constructor is required, such as std::map::operator[].
+ BoxedValue() : value_(NULL), deleter_(NULL), printer_(NULL) {}
+
+ // Creates a BoxedValue for the passed pointer |value|. The BoxedValue keeps
+ // the ownership of this pointer and can't be released.
+ template<typename T>
+ explicit BoxedValue(const T* value)
+ : value_(static_cast<const void*>(value)), deleter_(ValueDeleter<T>),
+ printer_(ValuePrinter<T>) {}
+
+ // The move constructor takes ownership of the pointer since the semantics of
+ // it allows to render the passed BoxedValue undefined. You need to use the
+ // move constructor explictly preventing it from accidental references,
+ // like in:
+ // BoxedValue new_box(std::move(other_box));
+ BoxedValue(BoxedValue&& other)
+ : value_(other.value_), deleter_(other.deleter_),
+ printer_(other.printer_) {
+ other.value_ = NULL;
+ other.deleter_ = NULL;
+ other.printer_ = NULL;
+ }
+
+ // Deletes the |value| passed on construction using the delete for the passed
+ // type.
+ ~BoxedValue() {
+ if (deleter_)
+ deleter_(value_);
+ }
+
+ const void* value() const { return value_; }
+
+ std::string ToString() const {
+ if (!printer_)
+ return "(no printer)";
+ if (!value_)
+ return "(no value)";
+ return printer_(value_);
+ }
+
+ // Static method to call the destructor of the right type.
+ template<typename T>
+ static void ValueDeleter(const void* value) {
+ delete reinterpret_cast<const T*>(value);
+ }
+
+ // Static method to print a type. See boxed_value.cc for common
+ // instantiations.
+ template<typename T>
+ static std::string ValuePrinter(const void* value);
+
+ private:
+ // A pointer to the cached value.
+ const void* value_;
+
+ // A function that calls delete for the right type of value_.
+ void (*deleter_)(const void*);
+
+ // A function that converts value_ to a string.
+ std::string (*printer_)(const void*);
+
+ DISALLOW_COPY_AND_ASSIGN(BoxedValue);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_BOXED_VALUE_H_
diff --git a/update_manager/boxed_value_unittest.cc b/update_manager/boxed_value_unittest.cc
new file mode 100644
index 0000000..b42b361
--- /dev/null
+++ b/update_manager/boxed_value_unittest.cc
@@ -0,0 +1,218 @@
+// 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 <gtest/gtest.h>
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+
+#include <base/strings/stringprintf.h>
+#include <base/time/time.h>
+
+#include "update_engine/update_manager/boxed_value.h"
+#include "update_engine/update_manager/shill_provider.h"
+#include "update_engine/update_manager/umtest_utils.h"
+#include "update_engine/update_manager/updater_provider.h"
+
+using base::Time;
+using base::TimeDelta;
+using std::list;
+using std::map;
+using std::set;
+using std::string;
+
+namespace chromeos_update_manager {
+
+// The DeleterMarker flags a bool variable when the class is destroyed.
+class DeleterMarker {
+ public:
+ DeleterMarker(bool* marker) : marker_(marker) { *marker_ = false; }
+
+ ~DeleterMarker() { *marker_ = true; }
+
+ private:
+ friend string BoxedValue::ValuePrinter<DeleterMarker>(const void *);
+
+ // Pointer to the bool marker.
+ bool* marker_;
+};
+
+template<>
+string BoxedValue::ValuePrinter<DeleterMarker>(const void *value) {
+ const DeleterMarker* val = reinterpret_cast<const DeleterMarker*>(value);
+ return base::StringPrintf("DeleterMarker:%s",
+ *val->marker_ ? "true" : "false");
+}
+
+TEST(UmBoxedValueTest, Deleted) {
+ bool marker = true;
+ const DeleterMarker* deleter_marker = new DeleterMarker(&marker);
+
+ EXPECT_FALSE(marker);
+ BoxedValue* box = new BoxedValue(deleter_marker);
+ EXPECT_FALSE(marker);
+ delete box;
+ EXPECT_TRUE(marker);
+}
+
+TEST(UmBoxedValueTest, MoveConstructor) {
+ bool marker = true;
+ const DeleterMarker* deleter_marker = new DeleterMarker(&marker);
+
+ BoxedValue* box = new BoxedValue(deleter_marker);
+ BoxedValue* new_box = new BoxedValue(std::move(*box));
+ // box is now undefined but valid.
+ delete box;
+ EXPECT_FALSE(marker);
+ // The deleter_marker gets deleted at this point.
+ delete new_box;
+ EXPECT_TRUE(marker);
+}
+
+TEST(UmBoxedValueTest, MixedList) {
+ list<BoxedValue> lst;
+ // This is mostly a compile test.
+ lst.emplace_back(new const int(42));
+ lst.emplace_back(new const string("Hello world!"));
+ bool marker;
+ lst.emplace_back(new const DeleterMarker(&marker));
+ EXPECT_FALSE(marker);
+ lst.clear();
+ EXPECT_TRUE(marker);
+}
+
+TEST(UmBoxedValueTest, MixedMap) {
+ map<int, BoxedValue> m;
+ m.emplace(42, std::move(BoxedValue(new const string("Hola mundo!"))));
+
+ auto it = m.find(42);
+ ASSERT_NE(it, m.end());
+ UMTEST_EXPECT_NOT_NULL(it->second.value());
+ UMTEST_EXPECT_NULL(m[33].value());
+}
+
+TEST(UmBoxedValueTest, StringToString) {
+ EXPECT_EQ("Hej Verden!",
+ BoxedValue(new string("Hej Verden!")).ToString());
+}
+
+TEST(UmBoxedValueTest, IntToString) {
+ EXPECT_EQ("42", BoxedValue(new int(42)).ToString());
+}
+
+TEST(UmBoxedValueTest, Int64ToString) {
+ // -123456789012345 doensn't fit in 32-bit integers.
+ EXPECT_EQ("-123456789012345", BoxedValue(
+ new int64_t(-123456789012345LL)).ToString());
+}
+
+TEST(UmBoxedValueTest, UnsignedIntToString) {
+ // 4294967295 is the biggest possible 32-bit unsigned integer.
+ EXPECT_EQ("4294967295", BoxedValue(new unsigned int(4294967295U)).ToString());
+}
+
+TEST(UmBoxedValueTest, UnsignedInt64ToString) {
+ // 18446744073709551615 is the biggest possible 64-bit unsigned integer.
+ EXPECT_EQ("18446744073709551615", BoxedValue(
+ new uint64_t(18446744073709551615ULL)).ToString());
+}
+
+TEST(UmBoxedValueTest, BoolToString) {
+ EXPECT_EQ("false", BoxedValue(new bool(false)).ToString());
+ EXPECT_EQ("true", BoxedValue(new bool(true)).ToString());
+}
+
+TEST(UmBoxedValueTest, DoubleToString) {
+ EXPECT_EQ("1.501", BoxedValue(new double(1.501)).ToString());
+}
+
+TEST(UmBoxedValueTest, TimeToString) {
+ // Tue Apr 29 22:30:55 UTC 2014 is 1398810655 seconds since the Unix Epoch.
+ EXPECT_EQ("4/29/2014 22:30:55 GMT",
+ BoxedValue(new Time(Time::FromTimeT(1398810655))).ToString());
+}
+
+TEST(UmBoxedValueTest, TimeDeltaToString) {
+ // 12345 seconds is 3 hours, 25 minutes and 45 seconds.
+ EXPECT_EQ("3h25m45s",
+ BoxedValue(new TimeDelta(TimeDelta::FromSeconds(12345)))
+ .ToString());
+}
+
+TEST(UmBoxedValueTest, ConnectionTypeToString) {
+ EXPECT_EQ("Ethernet",
+ BoxedValue(new ConnectionType(ConnectionType::kEthernet))
+ .ToString());
+ EXPECT_EQ("Wifi",
+ BoxedValue(new ConnectionType(ConnectionType::kWifi)).ToString());
+ EXPECT_EQ("Wimax",
+ BoxedValue(new ConnectionType(ConnectionType::kWimax)).ToString());
+ EXPECT_EQ("Bluetooth",
+ BoxedValue(new ConnectionType(ConnectionType::kBluetooth))
+ .ToString());
+ EXPECT_EQ("Cellular",
+ BoxedValue(new ConnectionType(ConnectionType::kCellular))
+ .ToString());
+ EXPECT_EQ("Unknown",
+ BoxedValue(new ConnectionType(ConnectionType::kUnknown))
+ .ToString());
+}
+
+TEST(UmBoxedValueTest, ConnectionTetheringToString) {
+ EXPECT_EQ("Not Detected",
+ BoxedValue(new ConnectionTethering(
+ ConnectionTethering::kNotDetected)).ToString());
+ EXPECT_EQ("Suspected",
+ BoxedValue(new ConnectionTethering(ConnectionTethering::kSuspected))
+ .ToString());
+ EXPECT_EQ("Confirmed",
+ BoxedValue(new ConnectionTethering(ConnectionTethering::kConfirmed))
+ .ToString());
+ EXPECT_EQ("Unknown",
+ BoxedValue(new ConnectionTethering(ConnectionTethering::kUnknown))
+ .ToString());
+}
+
+TEST(UmBoxedValueTest, SetConnectionTypeToString) {
+ set<ConnectionType>* set1 = new set<ConnectionType>;
+ set1->insert(ConnectionType::kWimax);
+ set1->insert(ConnectionType::kEthernet);
+ EXPECT_EQ("Ethernet,Wimax", BoxedValue(set1).ToString());
+
+ set<ConnectionType>* set2 = new set<ConnectionType>;
+ set2->insert(ConnectionType::kWifi);
+ EXPECT_EQ("Wifi", BoxedValue(set2).ToString());
+}
+
+TEST(UmBoxedValueTest, StageToString) {
+ EXPECT_EQ("Idle",
+ BoxedValue(new Stage(Stage::kIdle)).ToString());
+ EXPECT_EQ("Checking For Update",
+ BoxedValue(new Stage(Stage::kCheckingForUpdate)).ToString());
+ EXPECT_EQ("Update Available",
+ BoxedValue(new Stage(Stage::kUpdateAvailable)).ToString());
+ EXPECT_EQ("Downloading",
+ BoxedValue(new Stage(Stage::kDownloading)).ToString());
+ EXPECT_EQ("Verifying",
+ BoxedValue(new Stage(Stage::kVerifying)).ToString());
+ EXPECT_EQ("Finalizing",
+ BoxedValue(new Stage(Stage::kFinalizing)).ToString());
+ EXPECT_EQ("Updated, Need Reboot",
+ BoxedValue(new Stage(Stage::kUpdatedNeedReboot)).ToString());
+ EXPECT_EQ("Reporting Error Event",
+ BoxedValue(new Stage(Stage::kReportingErrorEvent)).ToString());
+ EXPECT_EQ("Attempting Rollback",
+ BoxedValue(new Stage(Stage::kAttemptingRollback)).ToString());
+}
+
+TEST(UmBoxedValueTest, DeleterMarkerToString) {
+ bool marker = false;
+ BoxedValue value = BoxedValue(new DeleterMarker(&marker));
+ EXPECT_EQ("DeleterMarker:false", value.ToString());
+ marker = true;
+ EXPECT_EQ("DeleterMarker:true", value.ToString());
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/chromeos_policy.cc b/update_manager/chromeos_policy.cc
new file mode 100644
index 0000000..6cf1fd1
--- /dev/null
+++ b/update_manager/chromeos_policy.cc
@@ -0,0 +1,396 @@
+// 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/update_manager/chromeos_policy.h"
+
+#include <algorithm>
+#include <set>
+#include <string>
+
+#include <base/logging.h>
+#include <base/time/time.h>
+
+#include "update_engine/update_manager/device_policy_provider.h"
+#include "update_engine/update_manager/policy_utils.h"
+#include "update_engine/update_manager/shill_provider.h"
+
+using base::Time;
+using base::TimeDelta;
+using std::min;
+using std::set;
+using std::string;
+
+namespace chromeos_update_manager {
+
+EvalStatus ChromeOSPolicy::UpdateCheckAllowed(
+ EvaluationContext* ec, State* state, string* error,
+ UpdateCheckParams* result) const {
+ Time next_update_check;
+ if (NextUpdateCheckTime(ec, state, error, &next_update_check) !=
+ EvalStatus::kSucceeded) {
+ return EvalStatus::kFailed;
+ }
+
+ if (!ec->IsTimeGreaterThan(next_update_check))
+ return EvalStatus::kAskMeAgainLater;
+
+ // It is time to check for an update.
+ result->updates_enabled = true;
+ return EvalStatus::kSucceeded;
+}
+
+EvalStatus ChromeOSPolicy::UpdateCanStart(
+ EvaluationContext* ec,
+ State* state,
+ string* error,
+ UpdateCanStartResult* result,
+ const bool interactive,
+ const UpdateState& update_state) const {
+ // Set the default return values.
+ result->update_can_start = true;
+ result->http_allowed = true;
+ result->p2p_allowed = false;
+ result->target_channel.clear();
+ result->cannot_start_reason = UpdateCannotStartReason::kUndefined;
+ result->scatter_wait_period = kZeroInterval;
+ result->scatter_check_threshold = 0;
+
+ // Make sure that we're not due for an update check.
+ UpdateCheckParams check_result;
+ EvalStatus check_status = UpdateCheckAllowed(ec, state, error, &check_result);
+ if (check_status == EvalStatus::kFailed)
+ return EvalStatus::kFailed;
+ if (check_status == EvalStatus::kSucceeded &&
+ check_result.updates_enabled == true) {
+ result->update_can_start = false;
+ result->cannot_start_reason = UpdateCannotStartReason::kCheckDue;
+ return EvalStatus::kSucceeded;
+ }
+
+ DevicePolicyProvider* const dp_provider = state->device_policy_provider();
+ SystemProvider* const system_provider = state->system_provider();
+
+ const bool* device_policy_is_loaded_p = ec->GetValue(
+ dp_provider->var_device_policy_is_loaded());
+ if (device_policy_is_loaded_p && *device_policy_is_loaded_p) {
+ // Ensure that update is enabled.
+ const bool* update_disabled_p = ec->GetValue(
+ dp_provider->var_update_disabled());
+ if (update_disabled_p && *update_disabled_p) {
+ result->update_can_start = false;
+ result->cannot_start_reason = UpdateCannotStartReason::kDisabledByPolicy;
+ return EvalStatus::kAskMeAgainLater;
+ }
+
+ // Check whether scattering applies to this update attempt. We should not be
+ // scattering if this is an interactive update check, or if OOBE is enabled
+ // but not completed.
+ //
+ // Note: current code further suppresses scattering if a "deadline"
+ // attribute is found in the Omaha response. However, it appears that the
+ // presence of this attribute is merely indicative of an OOBE update, during
+ // which we suppress scattering anyway.
+ bool scattering_applies = false;
+ if (!interactive) {
+ const bool* is_oobe_enabled_p = ec->GetValue(
+ state->config_provider()->var_is_oobe_enabled());
+ if (is_oobe_enabled_p && !(*is_oobe_enabled_p)) {
+ scattering_applies = true;
+ } else {
+ const bool* is_oobe_complete_p = ec->GetValue(
+ system_provider->var_is_oobe_complete());
+ scattering_applies = (is_oobe_complete_p && *is_oobe_complete_p);
+ }
+ }
+
+ // Compute scattering values.
+ if (scattering_applies) {
+ UpdateScatteringResult scatter_result;
+ EvalStatus scattering_status = UpdateScattering(
+ ec, state, error, &scatter_result, update_state);
+ if (scattering_status != EvalStatus::kSucceeded ||
+ scatter_result.is_scattering) {
+ if (scattering_status != EvalStatus::kFailed) {
+ result->update_can_start = false;
+ result->cannot_start_reason = UpdateCannotStartReason::kScattering;
+ result->scatter_wait_period = scatter_result.wait_period;
+ result->scatter_check_threshold = scatter_result.check_threshold;
+ }
+ return scattering_status;
+ }
+ }
+
+ // Determine whether HTTP downloads are forbidden by policy. This only
+ // applies to official system builds; otherwise, HTTP is always enabled.
+ const bool* is_official_build_p = ec->GetValue(
+ system_provider->var_is_official_build());
+ if (is_official_build_p && *is_official_build_p) {
+ const bool* policy_http_downloads_enabled_p = ec->GetValue(
+ dp_provider->var_http_downloads_enabled());
+ result->http_allowed =
+ !policy_http_downloads_enabled_p || *policy_http_downloads_enabled_p;
+ }
+
+ // Determine whether use of P2P is allowed by policy.
+ const bool* policy_au_p2p_enabled_p = ec->GetValue(
+ dp_provider->var_au_p2p_enabled());
+ result->p2p_allowed = policy_au_p2p_enabled_p && *policy_au_p2p_enabled_p;
+
+ // Determine whether a target channel is dictated by policy.
+ const bool* release_channel_delegated_p = ec->GetValue(
+ dp_provider->var_release_channel_delegated());
+ if (release_channel_delegated_p && !(*release_channel_delegated_p)) {
+ const string* release_channel_p = ec->GetValue(
+ dp_provider->var_release_channel());
+ if (release_channel_p)
+ result->target_channel = *release_channel_p;
+ }
+ }
+
+ // Enable P2P, if so mandated by the updater configuration.
+ if (!result->p2p_allowed) {
+ const bool* updater_p2p_enabled_p = ec->GetValue(
+ state->updater_provider()->var_p2p_enabled());
+ result->p2p_allowed = updater_p2p_enabled_p && *updater_p2p_enabled_p;
+ }
+
+ return EvalStatus::kSucceeded;
+}
+
+EvalStatus ChromeOSPolicy::NextUpdateCheckTime(EvaluationContext* ec,
+ State* state, string* error,
+ Time* next_update_check) const {
+ // Don't check for updates too often. We limit the update checks to once every
+ // some interval. The interval is kTimeoutInitialInterval the first time and
+ // kTimeoutPeriodicInterval for the subsequent update checks. If the update
+ // check fails, we increase the interval between the update checks
+ // exponentially until kTimeoutMaxBackoffInterval. Finally, to avoid having
+ // many chromebooks running update checks at the exact same time, we add some
+ // fuzz to the interval.
+ const Time* updater_started_time =
+ ec->GetValue(state->updater_provider()->var_updater_started_time());
+ POLICY_CHECK_VALUE_AND_FAIL(updater_started_time, error);
+
+ const base::Time* last_checked_time =
+ ec->GetValue(state->updater_provider()->var_last_checked_time());
+
+ const uint64_t* seed = ec->GetValue(state->random_provider()->var_seed());
+ POLICY_CHECK_VALUE_AND_FAIL(seed, error);
+
+ PRNG prng(*seed);
+
+ if (!last_checked_time || *last_checked_time < *updater_started_time) {
+ // First attempt.
+ *next_update_check = *updater_started_time + FuzzedInterval(
+ &prng, kTimeoutInitialInterval, kTimeoutRegularFuzz);
+ return EvalStatus::kSucceeded;
+ }
+ // Check for previous failed attempts to implement the exponential backoff.
+ const unsigned int* consecutive_failed_update_checks = ec->GetValue(
+ state->updater_provider()->var_consecutive_failed_update_checks());
+ POLICY_CHECK_VALUE_AND_FAIL(consecutive_failed_update_checks, error);
+
+ int interval = kTimeoutInitialInterval;
+ for (unsigned int i = 0; i < *consecutive_failed_update_checks; ++i) {
+ interval *= 2;
+ if (interval > kTimeoutMaxBackoffInterval) {
+ interval = kTimeoutMaxBackoffInterval;
+ break;
+ }
+ }
+
+ *next_update_check = *last_checked_time + FuzzedInterval(
+ &prng, interval, kTimeoutRegularFuzz);
+ return EvalStatus::kSucceeded;
+}
+
+TimeDelta ChromeOSPolicy::FuzzedInterval(PRNG* prng, int interval, int fuzz) {
+ DCHECK_GE(interval, 0);
+ DCHECK_GE(fuzz, 0);
+ int half_fuzz = fuzz / 2;
+ // This guarantees the output interval is non negative.
+ int interval_min = std::max(interval - half_fuzz, 0);
+ int interval_max = interval + half_fuzz;
+ return TimeDelta::FromSeconds(prng->RandMinMax(interval_min, interval_max));
+}
+
+EvalStatus ChromeOSPolicy::UpdateScattering(
+ EvaluationContext* ec,
+ State* state,
+ string* error,
+ UpdateScatteringResult* result,
+ const UpdateState& update_state) const {
+ // Preconditions. These stem from the postconditions and usage contract.
+ DCHECK(update_state.scatter_wait_period >= kZeroInterval);
+ DCHECK_GE(update_state.scatter_check_threshold, 0);
+
+ // Set default result values.
+ result->is_scattering = false;
+ result->wait_period = kZeroInterval;
+ result->check_threshold = 0;
+
+ DevicePolicyProvider* const dp_provider = state->device_policy_provider();
+
+ // Ensure that a device policy is loaded.
+ const bool* device_policy_is_loaded_p = ec->GetValue(
+ dp_provider->var_device_policy_is_loaded());
+ if (!(device_policy_is_loaded_p && *device_policy_is_loaded_p))
+ return EvalStatus::kSucceeded;
+
+ // Is scattering enabled by policy?
+ const TimeDelta* scatter_factor_p = ec->GetValue(
+ dp_provider->var_scatter_factor());
+ if (!scatter_factor_p || *scatter_factor_p == kZeroInterval)
+ return EvalStatus::kSucceeded;
+
+ // Obtain a pseudo-random number generator.
+ const uint64_t* seed = ec->GetValue(state->random_provider()->var_seed());
+ POLICY_CHECK_VALUE_AND_FAIL(seed, error);
+ PRNG prng(*seed);
+
+ // Step 1: Maintain the scattering wait period.
+ //
+ // If no wait period was previously determined, or it no longer fits in the
+ // scatter factor, then generate a new one. Otherwise, keep the one we have.
+ TimeDelta wait_period = update_state.scatter_wait_period;
+ if (wait_period == kZeroInterval || wait_period > *scatter_factor_p) {
+ wait_period = TimeDelta::FromSeconds(
+ prng.RandMinMax(1, scatter_factor_p->InSeconds()));
+ }
+
+ // If we surpass the wait period or the max scatter period associated with
+ // the update, then no wait is needed.
+ Time wait_expires = (update_state.first_seen +
+ min(wait_period, update_state.scatter_wait_period_max));
+ if (ec->IsTimeGreaterThan(wait_expires))
+ wait_period = kZeroInterval;
+
+ // Step 2: Maintain the update check threshold count.
+ //
+ // If an update check threshold is not specified then generate a new
+ // one.
+ int check_threshold = update_state.scatter_check_threshold;
+ if (check_threshold == 0) {
+ check_threshold = prng.RandMinMax(
+ update_state.scatter_check_threshold_min,
+ update_state.scatter_check_threshold_max);
+ }
+
+ // If the update check threshold is not within allowed range then nullify it.
+ // TODO(garnold) This is compliant with current logic found in
+ // OmahaRequestAction::IsUpdateCheckCountBasedWaitingSatisfied(). We may want
+ // to change it so that it behaves similarly to the wait period case, namely
+ // if the current value exceeds the maximum, we set a new one within range.
+ if (check_threshold > update_state.scatter_check_threshold_max)
+ check_threshold = 0;
+
+ // If the update check threshold is non-zero and satisfied, then nullify it.
+ if (check_threshold > 0 && update_state.num_checks >= check_threshold)
+ check_threshold = 0;
+
+ bool is_scattering = (wait_period != kZeroInterval || check_threshold);
+ EvalStatus ret = EvalStatus::kSucceeded;
+ if (is_scattering && wait_period == update_state.scatter_wait_period &&
+ check_threshold == update_state.scatter_check_threshold)
+ ret = EvalStatus::kAskMeAgainLater;
+ result->is_scattering = is_scattering;
+ result->wait_period = wait_period;
+ result->check_threshold = check_threshold;
+ return ret;
+}
+
+// TODO(garnold) Logic in this method is based on
+// ConnectionManager::IsUpdateAllowedOver(); be sure to deprecate the latter.
+//
+// TODO(garnold) The current logic generally treats the list of allowed
+// connections coming from the device policy as a whitelist, meaning that it
+// can only be used for enabling connections, but not disable them. Further,
+// certain connection types (like Bluetooth) cannot be enabled even by policy.
+// In effect, the only thing that device policy can change is to enable
+// updates over a cellular network (disabled by default). We may want to
+// revisit this semantics, allowing greater flexibility in defining specific
+// permissions over all types of networks.
+EvalStatus ChromeOSPolicy::UpdateCurrentConnectionAllowed(
+ EvaluationContext* ec,
+ State* state,
+ string* error,
+ bool* result) const {
+ // Get the current connection type.
+ ShillProvider* const shill_provider = state->shill_provider();
+ const ConnectionType* conn_type_p = ec->GetValue(
+ shill_provider->var_conn_type());
+ POLICY_CHECK_VALUE_AND_FAIL(conn_type_p, error);
+ ConnectionType conn_type = *conn_type_p;
+
+ // If we're tethering, treat it as a cellular connection.
+ if (conn_type != ConnectionType::kCellular) {
+ const ConnectionTethering* conn_tethering_p = ec->GetValue(
+ shill_provider->var_conn_tethering());
+ POLICY_CHECK_VALUE_AND_FAIL(conn_tethering_p, error);
+ if (*conn_tethering_p == ConnectionTethering::kConfirmed)
+ conn_type = ConnectionType::kCellular;
+ }
+
+ // By default, we allow updates for all connection types, with exceptions as
+ // noted below. This also determines whether a device policy can override the
+ // default.
+ *result = true;
+ bool device_policy_can_override = false;
+ switch (conn_type) {
+ case ConnectionType::kBluetooth:
+ *result = false;
+ break;
+
+ case ConnectionType::kCellular:
+ *result = false;
+ device_policy_can_override = true;
+ break;
+
+ case ConnectionType::kUnknown:
+ if (error)
+ *error = "Unknown connection type";
+ return EvalStatus::kFailed;
+
+ default:
+ break; // Nothing to do.
+ }
+
+ // If update is allowed, we're done.
+ if (*result)
+ return EvalStatus::kSucceeded;
+
+ // Check whether the device policy specifically allows this connection.
+ bool user_settings_can_override = false;
+ if (device_policy_can_override) {
+ DevicePolicyProvider* const dp_provider = state->device_policy_provider();
+ const bool* device_policy_is_loaded_p = ec->GetValue(
+ dp_provider->var_device_policy_is_loaded());
+ if (device_policy_is_loaded_p && *device_policy_is_loaded_p) {
+ const set<ConnectionType>* allowed_conn_types_p = ec->GetValue(
+ dp_provider->var_allowed_connection_types_for_update());
+ if (allowed_conn_types_p) {
+ if (allowed_conn_types_p->count(conn_type)) {
+ *result = true;
+ return EvalStatus::kSucceeded;
+ }
+ } else {
+ user_settings_can_override = true;
+ }
+ }
+ }
+
+ // Local user settings can allow updates iff a policy was loaded but no
+ // allowed connections were specified in it. In all other cases, we either
+ // stick with the default or use the values determined by the policy.
+ if (user_settings_can_override) {
+ const bool* update_over_cellular_allowed_p = ec->GetValue(
+ state->updater_provider()->var_cellular_enabled());
+ if (update_over_cellular_allowed_p && *update_over_cellular_allowed_p)
+ *result = true;
+ }
+
+ return EvalStatus::kSucceeded;
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/chromeos_policy.h b/update_manager/chromeos_policy.h
new file mode 100644
index 0000000..3c47c60
--- /dev/null
+++ b/update_manager/chromeos_policy.h
@@ -0,0 +1,111 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_CHROMEOS_POLICY_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_CHROMEOS_POLICY_H_
+
+#include <base/time/time.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "update_engine/update_manager/policy.h"
+#include "update_engine/update_manager/prng.h"
+
+namespace chromeos_update_manager {
+
+// Parameters for update scattering, as determined by UpdateNotScattering.
+struct UpdateScatteringResult {
+ bool is_scattering;
+ base::TimeDelta wait_period;
+ int check_threshold;
+};
+
+// ChromeOSPolicy implements the policy-related logic used in ChromeOS.
+class ChromeOSPolicy : public Policy {
+ public:
+ ChromeOSPolicy() {}
+ virtual ~ChromeOSPolicy() {}
+
+ // Policy overrides.
+ virtual EvalStatus UpdateCheckAllowed(
+ EvaluationContext* ec, State* state, std::string* error,
+ UpdateCheckParams* result) const override;
+
+ virtual EvalStatus UpdateCanStart(
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ UpdateCanStartResult* result,
+ const bool interactive,
+ const UpdateState& update_state) const override;
+
+ virtual EvalStatus UpdateCurrentConnectionAllowed(
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ bool* result) const override;
+
+ private:
+ friend class UmChromeOSPolicyTest;
+ FRIEND_TEST(UmChromeOSPolicyTest,
+ FirstCheckIsAtMostInitialIntervalAfterStart);
+ FRIEND_TEST(UmChromeOSPolicyTest, ExponentialBackoffIsCapped);
+ FRIEND_TEST(UmChromeOSPolicyTest, UpdateCheckAllowedWaitsForTheTimeout);
+ FRIEND_TEST(UmChromeOSPolicyTest,
+ UpdateCanStartNotAllowedScatteringNewWaitPeriodApplies);
+ FRIEND_TEST(UmChromeOSPolicyTest,
+ UpdateCanStartNotAllowedScatteringPrevWaitPeriodStillApplies);
+ FRIEND_TEST(UmChromeOSPolicyTest,
+ UpdateCanStartNotAllowedScatteringNewCountThresholdApplies);
+ FRIEND_TEST(UmChromeOSPolicyTest,
+ UpdateCanStartNotAllowedScatteringPrevCountThresholdStillApplies);
+ FRIEND_TEST(UmChromeOSPolicyTest, UpdateCanStartAllowedScatteringSatisfied);
+ FRIEND_TEST(UmChromeOSPolicyTest,
+ UpdateCanStartAllowedInteractivePreventsScattering);
+
+ // Auxiliary constant (zero by default).
+ const base::TimeDelta kZeroInterval;
+
+ // Default update check timeout interval/fuzz values used to compute the
+ // NextUpdateCheckTime(), in seconds. Actual fuzz is within +/- half of the
+ // indicated value.
+ static const int kTimeoutInitialInterval = 7 * 60;
+ static const int kTimeoutPeriodicInterval = 45 * 60;
+ static const int kTimeoutQuickInterval = 1 * 60;
+ static const int kTimeoutMaxBackoffInterval = 4 * 60 * 60;
+ static const int kTimeoutRegularFuzz = 10 * 60;
+
+ // A private policy implementation returning the wallclock timestamp when
+ // the next update check should happen.
+ EvalStatus NextUpdateCheckTime(EvaluationContext* ec, State* state,
+ std::string* error,
+ base::Time* next_update_check) const;
+
+ // Returns a TimeDelta based on the provided |interval| seconds +/- half
+ // |fuzz| seconds. The return value is guaranteed to be a non-negative
+ // TimeDelta.
+ static base::TimeDelta FuzzedInterval(PRNG* prng, int interval, int fuzz);
+
+ // A private policy for checking whether scattering is due. Writes in |result|
+ // the decision as to whether or not to scatter; a wallclock-based scatter
+ // wait period, which ranges from zero (do not wait) and no greater than the
+ // current scatter factor provided by the device policy (if available) or the
+ // maximum wait period determined by Omaha; and an update check-based
+ // threshold between zero (no threshold) and the maximum number determined by
+ // the update engine. Within |update_state|, |wait_period| should contain the
+ // last scattering period returned by this function, or zero if no wait period
+ // is known; |check_threshold| is the last update check threshold, or zero if
+ // no such threshold is known. If not scattering, or if any of the scattering
+ // values has changed, returns |EvalStatus::kSucceeded|; otherwise,
+ // |EvalStatus::kAskMeAgainLater|.
+ EvalStatus UpdateScattering(EvaluationContext* ec, State* state,
+ std::string* error,
+ UpdateScatteringResult* result,
+ const UpdateState& update_state) const;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeOSPolicy);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_CHROMEOS_POLICY_H_
diff --git a/update_manager/chromeos_policy_unittest.cc b/update_manager/chromeos_policy_unittest.cc
new file mode 100644
index 0000000..0fa96b3
--- /dev/null
+++ b/update_manager/chromeos_policy_unittest.cc
@@ -0,0 +1,701 @@
+// 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/update_manager/chromeos_policy.h"
+
+#include <set>
+#include <string>
+
+#include <base/time/time.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/fake_clock.h"
+#include "update_engine/update_manager/evaluation_context.h"
+#include "update_engine/update_manager/fake_state.h"
+#include "update_engine/update_manager/umtest_utils.h"
+
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_engine::FakeClock;
+using std::set;
+using std::string;
+
+namespace chromeos_update_manager {
+
+class UmChromeOSPolicyTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ SetUpDefaultClock();
+ eval_ctx_ = new EvaluationContext(&fake_clock_);
+ SetUpDefaultState();
+ SetUpDefaultDevicePolicy();
+ }
+
+ // Sets the clock to fixed values.
+ void SetUpDefaultClock() {
+ fake_clock_.SetMonotonicTime(Time::FromInternalValue(12345678L));
+ fake_clock_.SetWallclockTime(Time::FromInternalValue(12345678901234L));
+ }
+
+ void SetUpDefaultState() {
+ fake_state_.updater_provider()->var_updater_started_time()->reset(
+ new Time(fake_clock_.GetWallclockTime()));
+ fake_state_.updater_provider()->var_last_checked_time()->reset(
+ new Time(fake_clock_.GetWallclockTime()));
+ fake_state_.updater_provider()->var_consecutive_failed_update_checks()->
+ reset(new unsigned int(0));
+
+ fake_state_.random_provider()->var_seed()->reset(
+ new uint64_t(4)); // chosen by fair dice roll.
+ // guaranteed to be random.
+
+ // No device policy loaded by default.
+ fake_state_.device_policy_provider()->var_device_policy_is_loaded()->reset(
+ new bool(false));
+
+ // For the purpose of the tests, this is an official build and OOBE was
+ // completed.
+ fake_state_.system_provider()->var_is_official_build()->reset(
+ new bool(true));
+ fake_state_.system_provider()->var_is_oobe_complete()->reset(
+ new bool(true));
+
+ // Connection is wifi, untethered.
+ fake_state_.shill_provider()->var_conn_type()->
+ reset(new ConnectionType(ConnectionType::kWifi));
+ fake_state_.shill_provider()->var_conn_tethering()->
+ reset(new ConnectionTethering(ConnectionTethering::kNotDetected));
+ }
+
+ // Sets up a default device policy that does not impose any restrictions, nor
+ // enables any features (HTTP, P2P).
+ void SetUpDefaultDevicePolicy() {
+ fake_state_.device_policy_provider()->var_device_policy_is_loaded()->reset(
+ new bool(true));
+ fake_state_.device_policy_provider()->var_update_disabled()->reset(
+ new bool(false));
+ fake_state_.device_policy_provider()->
+ var_allowed_connection_types_for_update()->reset(nullptr);
+ fake_state_.device_policy_provider()->var_scatter_factor()->reset(
+ new TimeDelta());
+ fake_state_.device_policy_provider()->var_http_downloads_enabled()->reset(
+ new bool(false));
+ fake_state_.device_policy_provider()->var_au_p2p_enabled()->reset(
+ new bool(false));
+ fake_state_.device_policy_provider()->var_release_channel_delegated()->
+ reset(new bool(true));
+ }
+
+ // Configures the UpdateCheckAllowed policy to return a desired value by
+ // faking the current wall clock time as needed. Restores the default state.
+ // This is used when testing policies that depend on this one.
+ void SetUpdateCheckAllowed(bool allow_check) {
+ Time next_update_check;
+ ExpectPolicyStatus(EvalStatus::kSucceeded,
+ &ChromeOSPolicy::NextUpdateCheckTime,
+ &next_update_check);
+ SetUpDefaultState();
+ SetUpDefaultDevicePolicy();
+ Time curr_time = next_update_check;
+ if (allow_check)
+ curr_time += TimeDelta::FromSeconds(1);
+ else
+ curr_time -= TimeDelta::FromSeconds(1);
+ fake_clock_.SetWallclockTime(curr_time);
+ }
+
+ // Returns a default UpdateState structure: first seen time is calculated
+ // backward from the current wall clock time, update was seen just once, there
+ // is no scattering wait period and the max allowed is 7 days, there is no
+ // check threshold and none is allowed.
+ UpdateState GetDefaultUpdateState(TimeDelta update_first_seen_period) {
+ UpdateState update_state = {
+ fake_clock_.GetWallclockTime() - update_first_seen_period, 1,
+ TimeDelta(), TimeDelta::FromDays(7), 0, 0, 0
+ };
+ return update_state;
+ }
+
+ // Runs the passed |policy_method| policy and expects it to return the
+ // |expected| return value.
+ template<typename T, typename R, typename... Args>
+ void ExpectPolicyStatus(
+ EvalStatus expected,
+ T policy_method,
+ R* result, Args... args) {
+ string error = "<None>";
+ eval_ctx_->ResetEvaluation();
+ EXPECT_EQ(expected,
+ (policy_.*policy_method)(eval_ctx_, &fake_state_, &error, result,
+ args...))
+ << "Returned error: " << error
+ << "\nEvaluation context: " << eval_ctx_->DumpContext();
+ }
+
+ FakeClock fake_clock_;
+ FakeState fake_state_;
+ scoped_refptr<EvaluationContext> eval_ctx_;
+ ChromeOSPolicy policy_; // ChromeOSPolicy under test.
+};
+
+TEST_F(UmChromeOSPolicyTest, FirstCheckIsAtMostInitialIntervalAfterStart) {
+ Time next_update_check;
+
+ ExpectPolicyStatus(EvalStatus::kSucceeded,
+ &ChromeOSPolicy::NextUpdateCheckTime, &next_update_check);
+
+ EXPECT_LE(fake_clock_.GetWallclockTime(), next_update_check);
+ EXPECT_GE(fake_clock_.GetWallclockTime() + TimeDelta::FromSeconds(
+ ChromeOSPolicy::kTimeoutInitialInterval +
+ ChromeOSPolicy::kTimeoutRegularFuzz), next_update_check);
+}
+
+TEST_F(UmChromeOSPolicyTest, ExponentialBackoffIsCapped) {
+ Time next_update_check;
+
+ fake_state_.updater_provider()->var_consecutive_failed_update_checks()->
+ reset(new unsigned int(100));
+ ExpectPolicyStatus(EvalStatus::kSucceeded,
+ &ChromeOSPolicy::NextUpdateCheckTime, &next_update_check);
+
+ EXPECT_LE(fake_clock_.GetWallclockTime() + TimeDelta::FromSeconds(
+ ChromeOSPolicy::kTimeoutMaxBackoffInterval -
+ ChromeOSPolicy::kTimeoutRegularFuzz - 1), next_update_check);
+ EXPECT_GE(fake_clock_.GetWallclockTime() + TimeDelta::FromSeconds(
+ ChromeOSPolicy::kTimeoutMaxBackoffInterval +
+ ChromeOSPolicy::kTimeoutRegularFuzz), next_update_check);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedWaitsForTheTimeout) {
+ // We get the next update_check timestamp from the policy's private method
+ // and then we check the public method respects that value on the normal
+ // case.
+ Time next_update_check;
+ Time last_checked_time =
+ fake_clock_.GetWallclockTime() + TimeDelta::FromMinutes(1234);
+
+ fake_state_.updater_provider()->var_last_checked_time()->reset(
+ new Time(last_checked_time));
+ ExpectPolicyStatus(EvalStatus::kSucceeded,
+ &ChromeOSPolicy::NextUpdateCheckTime, &next_update_check);
+
+ UpdateCheckParams result;
+
+ // Check that the policy blocks until the next_update_check is reached.
+ SetUpDefaultClock();
+ SetUpDefaultState();
+ fake_state_.updater_provider()->var_last_checked_time()->reset(
+ new Time(last_checked_time));
+ fake_clock_.SetWallclockTime(next_update_check - TimeDelta::FromSeconds(1));
+ ExpectPolicyStatus(EvalStatus::kAskMeAgainLater,
+ &Policy::UpdateCheckAllowed, &result);
+
+ SetUpDefaultClock();
+ SetUpDefaultState();
+ fake_state_.updater_provider()->var_last_checked_time()->reset(
+ new Time(last_checked_time));
+ fake_clock_.SetWallclockTime(next_update_check + TimeDelta::FromSeconds(1));
+ ExpectPolicyStatus(EvalStatus::kSucceeded,
+ &Policy::UpdateCheckAllowed, &result);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartFailsCheckAllowedError) {
+ // The UpdateCanStart policy fails, not being able to query
+ // UpdateCheckAllowed.
+
+ // Configure the UpdateCheckAllowed policy to fail.
+ fake_state_.updater_provider()->var_updater_started_time()->reset(nullptr);
+
+ // Check that the UpdateCanStart fails.
+ UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+ UpdateCanStartResult result;
+ ExpectPolicyStatus(EvalStatus::kFailed,
+ &Policy::UpdateCanStart, &result, false, update_state);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartNotAllowedCheckDue) {
+ // The UpdateCanStart policy returns false because we are due for another
+ // update check.
+
+ SetUpdateCheckAllowed(true);
+
+ // Check that the UpdateCanStart returns false.
+ UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+ UpdateCanStartResult result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded,
+ &Policy::UpdateCanStart, &result, false, update_state);
+ EXPECT_FALSE(result.update_can_start);
+ EXPECT_EQ(UpdateCannotStartReason::kCheckDue, result.cannot_start_reason);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedNoDevicePolicy) {
+ // The UpdateCanStart policy returns true; no device policy is loaded.
+
+ SetUpdateCheckAllowed(false);
+ fake_state_.device_policy_provider()->var_device_policy_is_loaded()->reset(
+ new bool(false));
+
+ // Check that the UpdateCanStart returns true with no further attributes.
+ UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+ UpdateCanStartResult result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded,
+ &Policy::UpdateCanStart, &result, false, update_state);
+ EXPECT_TRUE(result.update_can_start);
+ EXPECT_TRUE(result.http_allowed);
+ EXPECT_FALSE(result.p2p_allowed);
+ EXPECT_TRUE(result.target_channel.empty());
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedBlankPolicy) {
+ // The UpdateCanStart policy returns true; device policy is loaded but imposes
+ // no restrictions on updating.
+
+ SetUpdateCheckAllowed(false);
+
+ // Check that the UpdateCanStart returns true.
+ UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+ UpdateCanStartResult result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded,
+ &Policy::UpdateCanStart, &result, false, update_state);
+ EXPECT_TRUE(result.update_can_start);
+ EXPECT_FALSE(result.http_allowed);
+ EXPECT_FALSE(result.p2p_allowed);
+ EXPECT_TRUE(result.target_channel.empty());
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartNotAllowedUpdatesDisabled) {
+ // The UpdateCanStart should return false (kAskMeAgainlater) because a device
+ // policy is loaded and prohibits updates.
+
+ SetUpdateCheckAllowed(false);
+ fake_state_.device_policy_provider()->var_update_disabled()->reset(
+ new bool(true));
+
+ // Check that the UpdateCanStart returns false.
+ UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+ UpdateCanStartResult result;
+ ExpectPolicyStatus(EvalStatus::kAskMeAgainLater,
+ &Policy::UpdateCanStart, &result, false, update_state);
+ EXPECT_FALSE(result.update_can_start);
+ EXPECT_EQ(UpdateCannotStartReason::kDisabledByPolicy,
+ result.cannot_start_reason);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartFailsScatteringFailed) {
+ // The UpdateCanStart policy fails because the UpdateScattering policy it
+ // depends on fails (unset variable).
+
+ SetUpdateCheckAllowed(false);
+
+ // Override the default seed variable with a null value so that the policy
+ // request would fail.
+ fake_state_.random_provider()->var_seed()->reset(nullptr);
+
+ // Check that the UpdateCanStart fails.
+ UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(1));
+ UpdateCanStartResult result;
+ ExpectPolicyStatus(EvalStatus::kFailed,
+ &Policy::UpdateCanStart, &result, false, update_state);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+ UpdateCanStartNotAllowedScatteringNewWaitPeriodApplies) {
+ // The UpdateCanStart policy returns false; device policy is loaded and
+ // scattering applies due to an unsatisfied wait period, which was newly
+ // generated.
+
+ SetUpdateCheckAllowed(false);
+ fake_state_.device_policy_provider()->var_scatter_factor()->reset(
+ new TimeDelta(TimeDelta::FromMinutes(2)));
+
+
+ UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(1));
+
+ // Check that the UpdateCanStart returns false and a new wait period
+ // generated.
+ UpdateCanStartResult result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+ false, update_state);
+ EXPECT_FALSE(result.update_can_start);
+ EXPECT_EQ(UpdateCannotStartReason::kScattering, result.cannot_start_reason);
+ EXPECT_LT(TimeDelta(), result.scatter_wait_period);
+ EXPECT_EQ(0, result.scatter_check_threshold);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+ UpdateCanStartNotAllowedScatteringPrevWaitPeriodStillApplies) {
+ // The UpdateCanStart policy returns false w/ kAskMeAgainLater; device policy
+ // is loaded and a previously generated scattering period still applies, none
+ // of the scattering values has changed.
+
+ SetUpdateCheckAllowed(false);
+ fake_state_.device_policy_provider()->var_scatter_factor()->reset(
+ new TimeDelta(TimeDelta::FromMinutes(2)));
+
+ UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(1));
+ update_state.scatter_wait_period = TimeDelta::FromSeconds(35);
+
+ // Check that the UpdateCanStart returns false and a new wait period
+ // generated.
+ UpdateCanStartResult result;
+ ExpectPolicyStatus(EvalStatus::kAskMeAgainLater, &Policy::UpdateCanStart,
+ &result, false, update_state);
+ EXPECT_FALSE(result.update_can_start);
+ EXPECT_EQ(UpdateCannotStartReason::kScattering, result.cannot_start_reason);
+ EXPECT_EQ(TimeDelta::FromSeconds(35), result.scatter_wait_period);
+ EXPECT_EQ(0, result.scatter_check_threshold);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+ UpdateCanStartNotAllowedScatteringNewCountThresholdApplies) {
+ // The UpdateCanStart policy returns false; device policy is loaded and
+ // scattering applies due to an unsatisfied update check count threshold.
+ //
+ // This ensures a non-zero check threshold, which may or may not be combined
+ // with a non-zero wait period (for which we cannot reliably control).
+
+ SetUpdateCheckAllowed(false);
+ fake_state_.device_policy_provider()->var_scatter_factor()->reset(
+ new TimeDelta(TimeDelta::FromSeconds(1)));
+
+ UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(1));
+ update_state.scatter_check_threshold_min = 2;
+ update_state.scatter_check_threshold_max = 5;
+
+ // Check that the UpdateCanStart returns false.
+ UpdateCanStartResult result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+ false, update_state);
+ EXPECT_FALSE(result.update_can_start);
+ EXPECT_EQ(UpdateCannotStartReason::kScattering, result.cannot_start_reason);
+ EXPECT_LE(2, result.scatter_check_threshold);
+ EXPECT_GE(5, result.scatter_check_threshold);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+ UpdateCanStartNotAllowedScatteringPrevCountThresholdStillApplies) {
+ // The UpdateCanStart policy returns false; device policy is loaded and
+ // scattering due to a previously generated count threshold still applies.
+
+ SetUpdateCheckAllowed(false);
+ fake_state_.device_policy_provider()->var_scatter_factor()->reset(
+ new TimeDelta(TimeDelta::FromSeconds(1)));
+
+ UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(1));
+ update_state.scatter_check_threshold = 3;
+ update_state.scatter_check_threshold_min = 2;
+ update_state.scatter_check_threshold_max = 5;
+
+ // Check that the UpdateCanStart returns false.
+ UpdateCanStartResult result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+ false, update_state);
+ EXPECT_FALSE(result.update_can_start);
+ EXPECT_EQ(UpdateCannotStartReason::kScattering, result.cannot_start_reason);
+ EXPECT_EQ(3, result.scatter_check_threshold);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedScatteringSatisfied) {
+ // The UpdateCanStart policy returns true; device policy is loaded and
+ // scattering is enabled, but both wait period and check threshold are
+ // satisfied.
+
+ SetUpdateCheckAllowed(false);
+ fake_state_.device_policy_provider()->var_scatter_factor()->reset(
+ new TimeDelta(TimeDelta::FromSeconds(120)));
+
+ UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(75));
+ update_state.num_checks = 4;
+ update_state.scatter_wait_period = TimeDelta::FromSeconds(60);
+ update_state.scatter_check_threshold = 3;
+ update_state.scatter_check_threshold_min = 2;
+ update_state.scatter_check_threshold_max = 5;
+
+ // Check that the UpdateCanStart returns true.
+ UpdateCanStartResult result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+ false, update_state);
+ EXPECT_TRUE(result.update_can_start);
+ EXPECT_EQ(TimeDelta(), result.scatter_wait_period);
+ EXPECT_EQ(0, result.scatter_check_threshold);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+ UpdateCanStartAllowedInteractivePreventsScattering) {
+ // The UpdateCanStart policy returns true; device policy is loaded and
+ // scattering would have applied, except that the update check is interactive
+ // and so it is suppressed.
+
+ SetUpdateCheckAllowed(false);
+ fake_state_.device_policy_provider()->var_scatter_factor()->reset(
+ new TimeDelta(TimeDelta::FromSeconds(1)));
+
+ UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(1));
+ update_state.scatter_check_threshold = 0;
+ update_state.scatter_check_threshold_min = 2;
+ update_state.scatter_check_threshold_max = 5;
+
+ // Check that the UpdateCanStart returns true.
+ UpdateCanStartResult result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+ true, update_state);
+ EXPECT_TRUE(result.update_can_start);
+ EXPECT_EQ(TimeDelta(), result.scatter_wait_period);
+ EXPECT_EQ(0, result.scatter_check_threshold);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+ UpdateCanStartAllowedOobePreventsScattering) {
+ // The UpdateCanStart policy returns true; device policy is loaded and
+ // scattering would have applied, except that OOBE was not completed and so it
+ // is suppressed.
+
+ SetUpdateCheckAllowed(false);
+ fake_state_.device_policy_provider()->var_scatter_factor()->reset(
+ new TimeDelta(TimeDelta::FromSeconds(1)));
+ fake_state_.system_provider()->var_is_oobe_complete()->reset(new bool(false));
+
+ UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromSeconds(1));
+ update_state.scatter_check_threshold = 0;
+ update_state.scatter_check_threshold_min = 2;
+ update_state.scatter_check_threshold_max = 5;
+
+ // Check that the UpdateCanStart returns true.
+ UpdateCanStartResult result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+ true, update_state);
+ EXPECT_TRUE(result.update_can_start);
+ EXPECT_EQ(TimeDelta(), result.scatter_wait_period);
+ EXPECT_EQ(0, result.scatter_check_threshold);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedWithAttributes) {
+ // The UpdateCanStart policy returns true; device policy permits both HTTP and
+ // P2P updates, as well as a non-empty target channel string.
+
+ SetUpdateCheckAllowed(false);
+
+ // Override specific device policy attributes.
+ fake_state_.device_policy_provider()->var_http_downloads_enabled()->reset(
+ new bool(true));
+ fake_state_.device_policy_provider()->var_au_p2p_enabled()->reset(
+ new bool(true));
+ fake_state_.device_policy_provider()->var_release_channel_delegated()->
+ reset(new bool(false));
+ fake_state_.device_policy_provider()->var_release_channel()->
+ reset(new string("foo-channel"));
+
+ // Check that the UpdateCanStart returns true.
+ UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+ UpdateCanStartResult result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+ false, update_state);
+ EXPECT_TRUE(result.update_can_start);
+ EXPECT_TRUE(result.http_allowed);
+ EXPECT_TRUE(result.p2p_allowed);
+ EXPECT_EQ("foo-channel", result.target_channel);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedWithP2PFromUpdater) {
+ // The UpdateCanStart policy returns true; device policy forbids both HTTP and
+ // P2P updates, but the updater is configured to allow P2P and overrules the
+ // setting.
+
+ SetUpdateCheckAllowed(false);
+
+ // Override specific device policy attributes.
+ fake_state_.device_policy_provider()->var_release_channel_delegated()->
+ reset(new bool(false));
+ fake_state_.device_policy_provider()->var_release_channel()->
+ reset(new string("foo-channel"));
+ fake_state_.updater_provider()->var_p2p_enabled()->reset(new bool(true));
+
+ // Check that the UpdateCanStart returns true.
+ UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+ UpdateCanStartResult result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+ false, update_state);
+ EXPECT_TRUE(result.update_can_start);
+ EXPECT_FALSE(result.http_allowed);
+ EXPECT_TRUE(result.p2p_allowed);
+ EXPECT_EQ("foo-channel", result.target_channel);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCanStartAllowedWithHttpForUnofficialBuild) {
+ // The UpdateCanStart policy returns true; device policy forbids both HTTP and
+ // P2P updates, but marking this an unofficial build overrules the HTTP
+ // setting.
+
+ SetUpdateCheckAllowed(false);
+
+ // Override specific device policy attributes.
+ fake_state_.device_policy_provider()->var_release_channel_delegated()->
+ reset(new bool(false));
+ fake_state_.device_policy_provider()->var_release_channel()->
+ reset(new string("foo-channel"));
+ fake_state_.system_provider()->var_is_official_build()->
+ reset(new bool(false));
+
+ // Check that the UpdateCanStart returns true.
+ UpdateState update_state = GetDefaultUpdateState(TimeDelta::FromMinutes(10));
+ UpdateCanStartResult result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded, &Policy::UpdateCanStart, &result,
+ false, update_state);
+ EXPECT_TRUE(result.update_can_start);
+ EXPECT_TRUE(result.http_allowed);
+ EXPECT_FALSE(result.p2p_allowed);
+ EXPECT_EQ("foo-channel", result.target_channel);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCurrentConnectionAllowedEthernetDefault) {
+ // Ethernet is always allowed.
+
+ fake_state_.shill_provider()->var_conn_type()->
+ reset(new ConnectionType(ConnectionType::kEthernet));
+
+ bool result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded,
+ &Policy::UpdateCurrentConnectionAllowed, &result);
+ EXPECT_TRUE(result);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCurrentConnectionAllowedWifiDefault) {
+ // Wifi is allowed if not tethered.
+
+ fake_state_.shill_provider()->var_conn_type()->
+ reset(new ConnectionType(ConnectionType::kWifi));
+
+ bool result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded,
+ &Policy::UpdateCurrentConnectionAllowed, &result);
+ EXPECT_TRUE(result);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+ UpdateCurrentConnectionNotAllowedWifiTetheredDefault) {
+ // Tethered wifi is not allowed by default.
+
+ fake_state_.shill_provider()->var_conn_type()->
+ reset(new ConnectionType(ConnectionType::kWifi));
+ fake_state_.shill_provider()->var_conn_tethering()->
+ reset(new ConnectionTethering(ConnectionTethering::kConfirmed));
+
+ bool result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded,
+ &Policy::UpdateCurrentConnectionAllowed, &result);
+ EXPECT_FALSE(result);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+ UpdateCurrentConnectionAllowedWifiTetheredPolicyOverride) {
+ // Tethered wifi can be allowed by policy.
+
+ fake_state_.shill_provider()->var_conn_type()->
+ reset(new ConnectionType(ConnectionType::kWifi));
+ fake_state_.shill_provider()->var_conn_tethering()->
+ reset(new ConnectionTethering(ConnectionTethering::kConfirmed));
+ set<ConnectionType> allowed_connections;
+ allowed_connections.insert(ConnectionType::kCellular);
+ fake_state_.device_policy_provider()->
+ var_allowed_connection_types_for_update()->
+ reset(new set<ConnectionType>(allowed_connections));
+
+ bool result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded,
+ &Policy::UpdateCurrentConnectionAllowed, &result);
+ EXPECT_TRUE(result);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCurrentConnectionAllowedWimaxDefault) {
+ // Wimax is always allowed.
+
+ fake_state_.shill_provider()->var_conn_type()->
+ reset(new ConnectionType(ConnectionType::kWifi));
+
+ bool result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded,
+ &Policy::UpdateCurrentConnectionAllowed, &result);
+ EXPECT_TRUE(result);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+ UpdateCurrentConnectionNotAllowedBluetoothDefault) {
+ // Bluetooth is never allowed.
+
+ fake_state_.shill_provider()->var_conn_type()->
+ reset(new ConnectionType(ConnectionType::kBluetooth));
+
+ bool result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded,
+ &Policy::UpdateCurrentConnectionAllowed, &result);
+ EXPECT_FALSE(result);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+ UpdateCurrentConnectionNotAllowedBluetoothPolicyCannotOverride) {
+ // Bluetooth cannot be allowed even by policy.
+
+ fake_state_.shill_provider()->var_conn_type()->
+ reset(new ConnectionType(ConnectionType::kBluetooth));
+ set<ConnectionType> allowed_connections;
+ allowed_connections.insert(ConnectionType::kBluetooth);
+ fake_state_.device_policy_provider()->
+ var_allowed_connection_types_for_update()->
+ reset(new set<ConnectionType>(allowed_connections));
+
+ bool result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded,
+ &Policy::UpdateCurrentConnectionAllowed, &result);
+ EXPECT_FALSE(result);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCurrentConnectionNotAllowedCellularDefault) {
+ // Cellular is not allowed by default.
+
+ fake_state_.shill_provider()->var_conn_type()->
+ reset(new ConnectionType(ConnectionType::kCellular));
+
+ bool result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded,
+ &Policy::UpdateCurrentConnectionAllowed, &result);
+ EXPECT_FALSE(result);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+ UpdateCurrentConnectionAllowedCellularPolicyOverride) {
+ // Update over cellular can be enabled by policy.
+
+ fake_state_.shill_provider()->var_conn_type()->
+ reset(new ConnectionType(ConnectionType::kCellular));
+ set<ConnectionType> allowed_connections;
+ allowed_connections.insert(ConnectionType::kCellular);
+ fake_state_.device_policy_provider()->
+ var_allowed_connection_types_for_update()->
+ reset(new set<ConnectionType>(allowed_connections));
+
+ bool result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded,
+ &Policy::UpdateCurrentConnectionAllowed, &result);
+ EXPECT_TRUE(result);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+ UpdateCurrentConnectionAllowedCellularUserOverride) {
+ // Update over cellular can be enabled by user settings, but only if policy
+ // is present and does not determine allowed connections.
+
+ fake_state_.shill_provider()->var_conn_type()->
+ reset(new ConnectionType(ConnectionType::kCellular));
+ set<ConnectionType> allowed_connections;
+ allowed_connections.insert(ConnectionType::kCellular);
+ fake_state_.updater_provider()->var_cellular_enabled()->
+ reset(new bool(true));
+
+ bool result;
+ ExpectPolicyStatus(EvalStatus::kSucceeded,
+ &Policy::UpdateCurrentConnectionAllowed, &result);
+ EXPECT_TRUE(result);
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/config_provider.h b/update_manager/config_provider.h
new file mode 100644
index 0000000..55a4d7a
--- /dev/null
+++ b/update_manager/config_provider.h
@@ -0,0 +1,31 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_CONFIG_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_CONFIG_PROVIDER_H_
+
+#include "update_engine/update_manager/provider.h"
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+// Provider for const system configurations. This provider reads the
+// configuration from a file on /etc.
+class ConfigProvider : public Provider {
+ public:
+ // Returns a variable stating whether the out of the box experience (OOBE) is
+ // enabled on this device. A value of false means that the device doesn't have
+ // an OOBE workflow.
+ virtual Variable<bool>* var_is_oobe_enabled() = 0;
+
+ protected:
+ ConfigProvider() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ConfigProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_CONFIG_PROVIDER_H_
diff --git a/update_manager/default_policy.h b/update_manager/default_policy.h
new file mode 100644
index 0000000..b3767dc
--- /dev/null
+++ b/update_manager/default_policy.h
@@ -0,0 +1,62 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_DEFAULT_POLICY_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_DEFAULT_POLICY_H_
+
+#include <base/time/time.h>
+
+#include "update_engine/update_manager/policy.h"
+
+namespace chromeos_update_manager {
+
+// The DefaultPolicy is a safe Policy implementation that doesn't fail. The
+// values returned by this policy are safe default in case of failure of the
+// actual policy being used by the UpdateManager.
+class DefaultPolicy : public Policy {
+ public:
+ DefaultPolicy() {}
+ virtual ~DefaultPolicy() {}
+
+ // Policy overrides.
+ virtual EvalStatus UpdateCheckAllowed(
+ EvaluationContext* ec, State* state, std::string* error,
+ UpdateCheckParams* result) const override {
+ result->updates_enabled = true;
+ return EvalStatus::kSucceeded;
+ }
+
+ virtual EvalStatus UpdateCanStart(
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ UpdateCanStartResult* result,
+ const bool interactive,
+ const UpdateState& update_state) const override {
+ result->update_can_start = true;
+ result->http_allowed = false;
+ result->p2p_allowed = false;
+ result->target_channel.clear();
+ result->cannot_start_reason = UpdateCannotStartReason::kUndefined;
+ result->scatter_wait_period = base::TimeDelta();
+ result->scatter_check_threshold = 0;
+ return EvalStatus::kSucceeded;
+ }
+
+ virtual EvalStatus UpdateCurrentConnectionAllowed(
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ bool* result) const override {
+ *result = true;
+ return EvalStatus::kSucceeded;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DefaultPolicy);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_DEFAULT_POLICY_H_
diff --git a/update_manager/device_policy_provider.h b/update_manager/device_policy_provider.h
new file mode 100644
index 0000000..f612d55
--- /dev/null
+++ b/update_manager/device_policy_provider.h
@@ -0,0 +1,63 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_DEVICE_POLICY_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_DEVICE_POLICY_PROVIDER_H_
+
+#include <set>
+#include <string>
+
+#include <base/time/time.h>
+#include <policy/libpolicy.h>
+
+#include "update_engine/update_manager/provider.h"
+#include "update_engine/update_manager/shill_provider.h"
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+// Provides access to the current DevicePolicy.
+class DevicePolicyProvider : public Provider {
+ public:
+ virtual ~DevicePolicyProvider() {}
+
+ // Variable stating whether the DevicePolicy was loaded.
+ virtual Variable<bool>* var_device_policy_is_loaded() = 0;
+
+ // Variables mapping the information received on the DevicePolicy protobuf.
+ virtual Variable<std::string>* var_release_channel() = 0;
+
+ virtual Variable<bool>* var_release_channel_delegated() = 0;
+
+ virtual Variable<bool>* var_update_disabled() = 0;
+
+ virtual Variable<std::string>* var_target_version_prefix() = 0;
+
+ // Returns a non-negative scatter interval used for updates.
+ virtual Variable<base::TimeDelta>* var_scatter_factor() = 0;
+
+ // Variable returing the set of connection types allowed for updates. The
+ // identifiers returned are consistent with the ones returned by the
+ // ShillProvider.
+ virtual Variable<std::set<ConnectionType>>*
+ var_allowed_connection_types_for_update() = 0;
+
+ // Variable stating the name of the device owner. For enterprise enrolled
+ // devices, this will be an empty string.
+ virtual Variable<std::string>* var_get_owner() = 0;
+
+ virtual Variable<bool>* var_http_downloads_enabled() = 0;
+
+ virtual Variable<bool>* var_au_p2p_enabled() = 0;
+
+ protected:
+ DevicePolicyProvider() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DevicePolicyProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_DEVICE_POLICY_PROVIDER_H_
diff --git a/update_manager/evaluation_context-inl.h b/update_manager/evaluation_context-inl.h
new file mode 100644
index 0000000..962d0e9
--- /dev/null
+++ b/update_manager/evaluation_context-inl.h
@@ -0,0 +1,41 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_EVALUATION_CONTEXT_INL_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_EVALUATION_CONTEXT_INL_H_
+
+#include <base/logging.h>
+
+namespace chromeos_update_manager {
+
+template<typename T>
+const T* EvaluationContext::GetValue(Variable<T>* var) {
+ if (var == nullptr) {
+ LOG(ERROR) << "GetValue received an uninitialized variable.";
+ return nullptr;
+ }
+
+ // Search for the value on the cache first.
+ ValueCacheMap::iterator it = value_cache_.find(var);
+ if (it != value_cache_.end())
+ return reinterpret_cast<const T*>(it->second.value());
+
+ // Get the value from the variable if not found on the cache.
+ std::string errmsg;
+ const T* result = var->GetValue(RemainingTime(), &errmsg);
+ if (result == nullptr) {
+ LOG(WARNING) << "Error reading Variable " << var->GetName() << ": \""
+ << errmsg << "\"";
+ }
+ // Cache the value for the next time. The map of CachedValues keeps the
+ // ownership of the pointer until the map is destroyed.
+ value_cache_.emplace(
+ static_cast<BaseVariable*>(var),
+ std::move(BoxedValue(result)));
+ return result;
+}
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_EVALUATION_CONTEXT_INL_H_
diff --git a/update_manager/evaluation_context.cc b/update_manager/evaluation_context.cc
new file mode 100644
index 0000000..4fc9a16
--- /dev/null
+++ b/update_manager/evaluation_context.cc
@@ -0,0 +1,163 @@
+// 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/update_manager/evaluation_context.h"
+
+#include <string>
+
+#include <base/bind.h>
+#include <base/json/json_writer.h>
+#include <base/values.h>
+
+#include "update_engine/utils.h"
+
+using base::Closure;
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_engine::ClockInterface;
+using std::string;
+
+namespace chromeos_update_manager {
+
+EvaluationContext::EvaluationContext(ClockInterface* clock)
+ : clock_(clock),
+ weak_ptr_factory_(this) {
+ ResetEvaluation();
+}
+
+EvaluationContext::~EvaluationContext() {
+ RemoveObserversAndTimeout();
+}
+
+void EvaluationContext::RemoveObserversAndTimeout() {
+ for (auto& it : value_cache_) {
+ if (it.first->GetMode() == kVariableModeAsync)
+ it.first->RemoveObserver(this);
+ }
+ CancelMainLoopEvent(poll_timeout_event_);
+ poll_timeout_event_ = kEventIdNull;
+}
+
+TimeDelta EvaluationContext::RemainingTime() const {
+ return evaluation_monotonic_deadline_ - clock_->GetMonotonicTime();
+}
+
+void EvaluationContext::ValueChanged(BaseVariable* var) {
+ DLOG(INFO) << "ValueChanged for variable " << var->GetName();
+ OnValueChangedOrPollTimeout();
+}
+
+void EvaluationContext::OnPollTimeout() {
+ DLOG(INFO) << "OnPollTimeout() called.";
+ poll_timeout_event_ = kEventIdNull;
+ OnValueChangedOrPollTimeout();
+}
+
+void EvaluationContext::OnValueChangedOrPollTimeout() {
+ RemoveObserversAndTimeout();
+
+ if (value_changed_callback_.get() != NULL) {
+ value_changed_callback_->Run();
+ value_changed_callback_.reset();
+ }
+}
+
+bool EvaluationContext::IsTimeGreaterThan(base::Time timestamp) {
+ if (evaluation_start_ > timestamp)
+ return true;
+ // We need to keep track of these calls to trigger a reevaluation.
+ if (reevaluation_time_ > timestamp)
+ reevaluation_time_ = timestamp;
+ return false;
+}
+
+void EvaluationContext::ResetEvaluation() {
+ // It is not important if these two values are not in sync. The first value is
+ // a reference in time when the evaluation started, to device time-based
+ // values for the current evaluation. The second is a deadline for the
+ // evaluation which required a monotonic source of time.
+ evaluation_start_ = clock_->GetWallclockTime();
+ evaluation_monotonic_deadline_ =
+ clock_->GetMonotonicTime() + evaluation_timeout_;
+ reevaluation_time_ = Time::Max();
+
+ // Remove the cached values of non-const variables
+ for (auto it = value_cache_.begin(); it != value_cache_.end(); ) {
+ if (it->first->GetMode() == kVariableModeConst) {
+ ++it;
+ } else {
+ it = value_cache_.erase(it);
+ }
+ }
+}
+
+bool EvaluationContext::RunOnValueChangeOrTimeout(Closure callback) {
+ TimeDelta reeval_timeout;
+ bool reeval_timeout_set = false;
+ bool waiting_for_value_change = false;
+
+ // Check if a reevaluation should be triggered due to a IsTimeGreaterThan()
+ // call.
+ if (reevaluation_time_ != Time::Max()) {
+ reeval_timeout = reevaluation_time_ - evaluation_start_;
+ reeval_timeout_set = true;
+ }
+
+ if (value_changed_callback_.get() != NULL) {
+ LOG(ERROR) << "RunOnValueChangeOrTimeout called more than once.";
+ return false;
+ }
+
+ for (auto& it : value_cache_) {
+ switch (it.first->GetMode()) {
+ case kVariableModeAsync:
+ waiting_for_value_change = true;
+ DLOG(INFO) << "Waiting for value on " << it.first->GetName();
+ it.first->AddObserver(this);
+ break;
+ case kVariableModePoll:
+ if (!reeval_timeout_set || reeval_timeout > it.first->GetPollInterval())
+ reeval_timeout = it.first->GetPollInterval();
+ reeval_timeout_set = true;
+ break;
+ case kVariableModeConst:
+ // Ignored.
+ break;
+ }
+ }
+ // Check if the re-evaluation is actually being scheduled. If there are no
+ // events waited for, this function should return false.
+ if (!waiting_for_value_change && !reeval_timeout_set)
+ return false;
+ if (reeval_timeout_set) {
+ poll_timeout_event_ = RunFromMainLoopAfterTimeout(
+ base::Bind(&EvaluationContext::OnPollTimeout,
+ weak_ptr_factory_.GetWeakPtr()),
+ reeval_timeout);
+ }
+
+ value_changed_callback_.reset(new Closure(callback));
+ return true;
+}
+
+string EvaluationContext::DumpContext() const {
+ base::DictionaryValue* variables = new base::DictionaryValue();
+ for (auto& it : value_cache_) {
+ variables->SetString(it.first->GetName(), it.second.ToString());
+ }
+
+ base::DictionaryValue value;
+ value.Set("variables", variables); // Adopts |variables|.
+ value.SetString("evaluation_start",
+ chromeos_update_engine::utils::ToString(evaluation_start_));
+
+ string json_str;
+ base::JSONWriter::WriteWithOptions(&value,
+ base::JSONWriter::OPTIONS_PRETTY_PRINT,
+ &json_str);
+
+ return json_str;
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/evaluation_context.h b/update_manager/evaluation_context.h
new file mode 100644
index 0000000..97e5b97
--- /dev/null
+++ b/update_manager/evaluation_context.h
@@ -0,0 +1,159 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_EVALUATION_CONTEXT_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_EVALUATION_CONTEXT_H_
+
+#include <map>
+
+#include <base/callback.h>
+#include <base/memory/ref_counted.h>
+#include <base/memory/scoped_ptr.h>
+#include <base/memory/weak_ptr.h>
+#include <base/time/time.h>
+
+#include "update_engine/clock_interface.h"
+#include "update_engine/update_manager/boxed_value.h"
+#include "update_engine/update_manager/event_loop.h"
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+// The EvaluationContext class is the interface between a policy implementation
+// and the state. The EvaluationContext tracks the variables used by a policy
+// request and caches the returned values, owning those cached values.
+// The same EvaluationContext should be re-used for all the evaluations of the
+// same policy request (an AsyncPolicyRequest might involve several
+// re-evaluations). Each evaluation of the EvaluationContext is run at a given
+// point in time, which is used as a reference for the evaluation timeout and
+// the time based queries of the policy, such as IsTimeGreaterThan().
+//
+// Example:
+//
+// scoped_refptr<EvaluationContext> ec = new EvaluationContext;
+//
+// ...
+// // The following call to ResetEvaluation() is optional. Use it to reset the
+// // evaluation time if the EvaluationContext isn't used right after its
+// // construction.
+// ec->ResetEvaluation();
+// EvalStatus status = policy->SomeMethod(ec, state, &result, args...);
+//
+// ...
+// // Run a closure when any of the used async variables changes its value or
+// // the timeout for requery the values happens again.
+// ec->RunOnValueChangeOrTimeout(closure);
+// // If the provided |closure| wants to re-evaluate the policy, it should
+// // call ec->ResetEvaluation() to start a new evaluation.
+//
+class EvaluationContext :
+ public base::RefCounted<EvaluationContext>,
+ private BaseVariable::ObserverInterface {
+ public:
+ explicit EvaluationContext(chromeos_update_engine::ClockInterface* clock);
+ ~EvaluationContext();
+
+ // Returns a pointer to the value returned by the passed variable |var|. The
+ // EvaluationContext instance keeps the ownership of the returned object. The
+ // returned object is valid during the life of the evaluation, even if the
+ // passed Variable changes it.
+ //
+ // In case of error, a NULL value is returned.
+ template<typename T>
+ const T* GetValue(Variable<T>* var);
+
+ // Returns whether the passed |timestamp| is greater than the evaluation
+ // time. The |timestamp| value should be in the same scale as the values
+ // returned by ClockInterface::GetWallclockTime().
+ bool IsTimeGreaterThan(base::Time timestamp);
+
+ // TODO(deymo): Move the following methods to an interface only visible by the
+ // UpdateManager class and not the policy implementations.
+
+ // Resets the EvaluationContext to its initial state removing all the
+ // non-const cached variables and re-setting the evaluation time. This should
+ // be called right before any new evaluation starts.
+ void ResetEvaluation();
+
+ // Schedules the passed |callback| closure to be called when a cached
+ // variable changes its value or a polling interval passes. If none of these
+ // events can happen, for example if there's no cached variable, this method
+ // returns false.
+ //
+ // Right before the passed closure is called the EvaluationContext is
+ // reseted, removing all the non-const cached values.
+ bool RunOnValueChangeOrTimeout(base::Closure callback);
+
+ // Returns a textual representation of the evaluation context,
+ // including the variables and their values. This is intended only
+ // to help with debugging and the format may change in the future.
+ std::string DumpContext() const;
+
+ private:
+ // Removes all the Observers and timeout callbacks scheduled by
+ // RunOnValueChangeOrTimeout(). This method is idempotent.
+ void RemoveObserversAndTimeout();
+
+ // BaseVariable::ObserverInterface override.
+ void ValueChanged(BaseVariable* var);
+
+ // Called from the main loop when the scheduled poll timeout has passed.
+ void OnPollTimeout();
+
+ // Removes the observers from the used Variables and cancels the poll timeout
+ // and executes the scheduled callback, if any.
+ void OnValueChangedOrPollTimeout();
+
+ // The remaining time for the current evaluation.
+ base::TimeDelta RemainingTime() const;
+
+ // A map to hold the cached values for every variable.
+ typedef std::map<BaseVariable*, BoxedValue> ValueCacheMap;
+
+ // The cached values of the called Variables.
+ ValueCacheMap value_cache_;
+
+ // A pointer to a copy of the closure passed to RunOnValueChangeOrTimeout().
+ scoped_ptr<base::Closure> value_changed_callback_;
+
+ // The EventId returned by the event loop identifying the timeout callback.
+ // Used to cancel the timeout callback.
+ EventId poll_timeout_event_ = kEventIdNull;
+
+ // Pointer to the mockable clock interface;
+ chromeos_update_engine::ClockInterface* clock_;
+
+ // The timestamp when the evaluation of this EvaluationContext started. This
+ // value is reset every time ResetEvaluation() is called. The time source
+ // used is the ClockInterface::GetWallclockTime().
+ base::Time evaluation_start_;
+
+ // The timestamp measured on the GetWallclockTime() scale, when a reevaluation
+ // should be triggered due to IsTimeGreaterThan() calls value changes. This
+ // timestamp is greater or equal to |evaluation_start_| since it is a
+ // timestamp in the future, but it can be lower than the current
+ // GetWallclockTime() at some point of the evaluation.
+ base::Time reevaluation_time_;
+
+ // The timeout of an evaluation, used to compute the RemainingTime() of an
+ // evaluation.
+ // TODO(deymo): Receive the timeout from the UpdateManager. crbug.com/363790
+ base::TimeDelta evaluation_timeout_ = base::TimeDelta::FromSeconds(5);
+
+ // The timestamp in the ClockInterface::GetMonotonicTime() scale at which the
+ // current evaluation should finish. This is used to compute the
+ // RemainingTime().
+ base::Time evaluation_monotonic_deadline_;
+
+ base::WeakPtrFactory<EvaluationContext> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(EvaluationContext);
+};
+
+} // namespace chromeos_update_manager
+
+// Include the implementation of the template methods.
+#include "update_engine/update_manager/evaluation_context-inl.h"
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_EVALUATION_CONTEXT_H_
diff --git a/update_manager/evaluation_context_unittest.cc b/update_manager/evaluation_context_unittest.cc
new file mode 100644
index 0000000..33d91a4
--- /dev/null
+++ b/update_manager/evaluation_context_unittest.cc
@@ -0,0 +1,335 @@
+// 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 <string>
+
+#include <base/bind.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/fake_clock.h"
+#include "update_engine/test_utils.h"
+#include "update_engine/update_manager/evaluation_context.h"
+#include "update_engine/update_manager/fake_variable.h"
+#include "update_engine/update_manager/generic_variables.h"
+#include "update_engine/update_manager/mock_variable.h"
+#include "update_engine/update_manager/umtest_utils.h"
+
+using base::Bind;
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_engine::FakeClock;
+using chromeos_update_engine::RunGMainLoopMaxIterations;
+using chromeos_update_engine::RunGMainLoopUntil;
+using std::string;
+using testing::Return;
+using testing::StrictMock;
+using testing::_;
+
+namespace {
+
+void DoNothing() {}
+
+// Sets the value of the passed pointer to true.
+void SetTrue(bool* value) {
+ *value = true;
+}
+
+bool GetBoolean(bool* value) {
+ return *value;
+}
+
+} // namespace
+
+namespace chromeos_update_manager {
+
+class UmEvaluationContextTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Set the clock to a fixed values.
+ fake_clock_.SetMonotonicTime(Time::FromInternalValue(12345678L));
+ // Mar 2, 2006 1:23:45 UTC is 1141262625 since the Unix Epoch.
+ fake_clock_.SetWallclockTime(Time::FromTimeT(1141262625));
+ eval_ctx_ = new EvaluationContext(&fake_clock_);
+ }
+
+ virtual void TearDown() {
+ eval_ctx_ = NULL;
+ // Check that the evaluation context removed all the observers.
+ EXPECT_TRUE(fake_int_var_.observer_list_.empty());
+ EXPECT_TRUE(fake_async_var_.observer_list_.empty());
+ EXPECT_TRUE(fake_const_var_.observer_list_.empty());
+ EXPECT_TRUE(fake_poll_var_.observer_list_.empty());
+ }
+
+ // TODO(deymo): Update the default timeout to the one passed on construction.
+ // See crbug.com/363790
+ base::TimeDelta default_timeout_ = base::TimeDelta::FromSeconds(5);
+
+ FakeClock fake_clock_;
+ scoped_refptr<EvaluationContext> eval_ctx_;
+
+ // FakeVariables used for testing the EvaluationContext. These are required
+ // here to prevent them from going away *before* the EvaluationContext under
+ // test does, which keeps a reference to them.
+ FakeVariable<bool> fail_var_ = {"fail_var", kVariableModePoll};
+ FakeVariable<int> fake_int_var_ = {"fake_int", kVariableModePoll};
+ FakeVariable<string> fake_async_var_ = {"fake_async", kVariableModeAsync};
+ FakeVariable<string> fake_const_var_ = {"fake_const", kVariableModeConst};
+ FakeVariable<string> fake_poll_var_ = {"fake_poll",
+ TimeDelta::FromSeconds(1)};
+ StrictMock<MockVariable<string>> mock_var_async_{"mock_var_async",
+ kVariableModeAsync};
+ StrictMock<MockVariable<string>> mock_var_poll_{"mock_var_poll",
+ kVariableModePoll};
+};
+
+TEST_F(UmEvaluationContextTest, GetValueFails) {
+ // FakeVariable is initialized as returning NULL.
+ UMTEST_EXPECT_NULL(eval_ctx_->GetValue(&fake_int_var_));
+}
+
+TEST_F(UmEvaluationContextTest, GetValueFailsWithInvalidVar) {
+ UMTEST_EXPECT_NULL(eval_ctx_->GetValue(
+ reinterpret_cast<Variable<int>*>(NULL)));
+}
+
+TEST_F(UmEvaluationContextTest, GetValueReturns) {
+ const int* p_fake_int;
+
+ fake_int_var_.reset(new int(42));
+ p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
+ UMTEST_ASSERT_NOT_NULL(p_fake_int);
+ EXPECT_EQ(42, *p_fake_int);
+}
+
+TEST_F(UmEvaluationContextTest, GetValueCached) {
+ const int* p_fake_int;
+
+ fake_int_var_.reset(new int(42));
+ p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
+
+ // Check that if the variable changes, the EvaluationContext keeps returning
+ // the cached value.
+ fake_int_var_.reset(new int(5));
+
+ p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
+ UMTEST_ASSERT_NOT_NULL(p_fake_int);
+ EXPECT_EQ(42, *p_fake_int);
+}
+
+TEST_F(UmEvaluationContextTest, GetValueCachesNull) {
+ const int* p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
+ UMTEST_EXPECT_NULL(p_fake_int);
+
+ fake_int_var_.reset(new int(42));
+ // A second attempt to read the variable should not work because this
+ // EvaluationContext already got a NULL value.
+ p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
+ UMTEST_EXPECT_NULL(p_fake_int);
+}
+
+TEST_F(UmEvaluationContextTest, GetValueMixedTypes) {
+ const int* p_fake_int;
+ const string* p_fake_string;
+
+ fake_int_var_.reset(new int(42));
+ fake_poll_var_.reset(new string("Hello world!"));
+ // Check that the EvaluationContext can handle multiple Variable types. This
+ // is mostly a compile-time check due to the template nature of this method.
+ p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
+ p_fake_string = eval_ctx_->GetValue(&fake_poll_var_);
+
+ UMTEST_ASSERT_NOT_NULL(p_fake_int);
+ EXPECT_EQ(42, *p_fake_int);
+
+ UMTEST_ASSERT_NOT_NULL(p_fake_string);
+ EXPECT_EQ("Hello world!", *p_fake_string);
+}
+
+// Test that we don't schedule an event if there's no variable to wait for.
+TEST_F(UmEvaluationContextTest, RunOnValueChangeOrTimeoutWithoutVariablesTest) {
+ fake_const_var_.reset(new string("Hello world!"));
+ EXPECT_EQ(*eval_ctx_->GetValue(&fake_const_var_), "Hello world!");
+
+ EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&DoNothing)));
+}
+
+// Test that we don't schedule an event if there's no variable to wait for.
+TEST_F(UmEvaluationContextTest, RunOnValueChangeOrTimeoutWithVariablesTest) {
+ fake_async_var_.reset(new string("Async value"));
+ eval_ctx_->GetValue(&fake_async_var_);
+
+ bool value = false;
+ EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
+ // Check that the scheduled callback isn't run until we signal a ValueChaged.
+ RunGMainLoopMaxIterations(100);
+ EXPECT_FALSE(value);
+
+ fake_async_var_.NotifyValueChanged();
+ EXPECT_FALSE(value);
+ // Ensure that the scheduled callback isn't run until we are back on the main
+ // loop.
+ RunGMainLoopMaxIterations(100);
+ EXPECT_TRUE(value);
+}
+
+// Test that we don't re-schedule the events if we are attending one.
+TEST_F(UmEvaluationContextTest, RunOnValueChangeOrTimeoutCalledTwiceTest) {
+ fake_async_var_.reset(new string("Async value"));
+ eval_ctx_->GetValue(&fake_async_var_);
+
+ bool value = false;
+ EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
+ EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
+
+ // The scheduled event should still work.
+ fake_async_var_.NotifyValueChanged();
+ RunGMainLoopMaxIterations(100);
+ EXPECT_TRUE(value);
+}
+
+// Test that we clear the events when destroying the EvaluationContext.
+TEST_F(UmEvaluationContextTest, RemoveObserversAndTimeoutTest) {
+ fake_async_var_.reset(new string("Async value"));
+ eval_ctx_->GetValue(&fake_async_var_);
+
+ bool value = false;
+ EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
+ eval_ctx_ = NULL;
+
+ // This should not trigger the callback since the EvaluationContext waiting
+ // for it is gone, and it should have remove all its observers.
+ fake_async_var_.NotifyValueChanged();
+ RunGMainLoopMaxIterations(100);
+ EXPECT_FALSE(value);
+}
+
+// Test that we don't schedule an event if there's no variable to wait for.
+TEST_F(UmEvaluationContextTest, RunOnValueChangeOrTimeoutRunsFromTimeoutTest) {
+ fake_poll_var_.reset(new string("Polled value"));
+ eval_ctx_->GetValue(&fake_poll_var_);
+
+ bool value = false;
+ EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
+ // Check that the scheduled callback isn't run until the timeout occurs.
+ RunGMainLoopMaxIterations(10);
+ EXPECT_FALSE(value);
+ RunGMainLoopUntil(10000, Bind(&GetBoolean, &value));
+ EXPECT_TRUE(value);
+}
+
+// Test that we can delete the EvaluationContext while having pending events.
+TEST_F(UmEvaluationContextTest, ObjectDeletedWithPendingEventsTest) {
+ fake_async_var_.reset(new string("Async value"));
+ fake_poll_var_.reset(new string("Polled value"));
+ eval_ctx_->GetValue(&fake_async_var_);
+ eval_ctx_->GetValue(&fake_poll_var_);
+ EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&DoNothing)));
+ // TearDown() checks for leaked observers on this async_variable, which means
+ // that our object is still alive after removing its reference.
+}
+
+// Test that timed events fired after removal of the EvaluationContext don't
+// crash.
+TEST_F(UmEvaluationContextTest, TimeoutEventAfterDeleteTest) {
+ FakeVariable<string> fake_short_poll_var = {"fake_short_poll", TimeDelta()};
+ fake_short_poll_var.reset(new string("Polled value"));
+ eval_ctx_->GetValue(&fake_short_poll_var);
+ bool value = false;
+ EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
+ // Remove the last reference to the EvaluationContext and run the loop for
+ // 1 second to give time to the main loop to trigger the timeout Event (of 0
+ // seconds). Our callback should not be called because the EvaluationContext
+ // was removed before the timeout event is attended.
+ eval_ctx_ = NULL;
+ RunGMainLoopUntil(1000, Bind(&GetBoolean, &value));
+ EXPECT_FALSE(value);
+}
+
+TEST_F(UmEvaluationContextTest, DefaultTimeout) {
+ // Test that the RemainingTime() uses the default timeout on setup.
+ EXPECT_CALL(mock_var_async_, GetValue(default_timeout_, _))
+ .WillOnce(Return(nullptr));
+ UMTEST_EXPECT_NULL(eval_ctx_->GetValue(&mock_var_async_));
+}
+
+TEST_F(UmEvaluationContextTest, TimeoutUpdatesWithMonotonicTime) {
+ fake_clock_.SetMonotonicTime(
+ fake_clock_.GetMonotonicTime() + TimeDelta::FromSeconds(1));
+
+ TimeDelta timeout = default_timeout_ - TimeDelta::FromSeconds(1);
+
+ EXPECT_CALL(mock_var_async_, GetValue(timeout, _))
+ .WillOnce(Return(nullptr));
+ UMTEST_EXPECT_NULL(eval_ctx_->GetValue(&mock_var_async_));
+}
+
+TEST_F(UmEvaluationContextTest, ResetEvaluationResetsTimes) {
+ base::Time cur_time = fake_clock_.GetWallclockTime();
+ // Advance the time on the clock but don't call ResetEvaluation yet.
+ fake_clock_.SetWallclockTime(cur_time + TimeDelta::FromSeconds(4));
+
+ EXPECT_TRUE(eval_ctx_->IsTimeGreaterThan(cur_time -
+ TimeDelta::FromSeconds(1)));
+ EXPECT_FALSE(eval_ctx_->IsTimeGreaterThan(cur_time));
+ EXPECT_FALSE(eval_ctx_->IsTimeGreaterThan(cur_time +
+ TimeDelta::FromSeconds(1)));
+ // Call ResetEvaluation now, which should use the new evaluation time.
+ eval_ctx_->ResetEvaluation();
+
+ cur_time = fake_clock_.GetWallclockTime();
+ EXPECT_TRUE(eval_ctx_->IsTimeGreaterThan(cur_time -
+ TimeDelta::FromSeconds(1)));
+ EXPECT_FALSE(eval_ctx_->IsTimeGreaterThan(cur_time));
+ EXPECT_FALSE(eval_ctx_->IsTimeGreaterThan(cur_time +
+ TimeDelta::FromSeconds(1)));
+}
+
+TEST_F(UmEvaluationContextTest, IsTimeGreaterThanSignalsTriggerReevaluation) {
+ EXPECT_FALSE(eval_ctx_->IsTimeGreaterThan(
+ fake_clock_.GetWallclockTime() + TimeDelta::FromSeconds(1)));
+
+ // The "false" from IsTimeGreaterThan means that's not that timestamp yet, so
+ // this should schedule a callback for when that happens.
+ EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&DoNothing)));
+}
+
+TEST_F(UmEvaluationContextTest, IsTimeGreaterThanDoesntRecordPastTimestamps) {
+ // IsTimeGreaterThan() should ignore timestamps on the past for reevaluation.
+ EXPECT_TRUE(eval_ctx_->IsTimeGreaterThan(
+ fake_clock_.GetWallclockTime() - TimeDelta::FromSeconds(20)));
+ EXPECT_TRUE(eval_ctx_->IsTimeGreaterThan(
+ fake_clock_.GetWallclockTime() - TimeDelta::FromSeconds(1)));
+
+ // Callback should not be scheduled.
+ EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&DoNothing)));
+}
+
+TEST_F(UmEvaluationContextTest, DumpContext) {
+ // |fail_var_| yield "(no value)" since it is unset.
+ eval_ctx_->GetValue(&fail_var_);
+
+ // Check that this is included.
+ fake_int_var_.reset(new int(42));
+ eval_ctx_->GetValue(&fake_int_var_);
+
+ // Check that double-quotes are escaped properly.
+ fake_poll_var_.reset(new string("Hello \"world\"!"));
+ eval_ctx_->GetValue(&fake_poll_var_);
+
+ // Note that the variables are printed in alphabetical order. Also
+ // see UmEvaluationContextText::SetUp() where the value used for
+ // |evaluation_start| is set.
+ EXPECT_EQ("{\n"
+ " \"evaluation_start\": \"3/2/2006 1:23:45 GMT\",\n"
+ " \"variables\": {\n"
+ " \"fail_var\": \"(no value)\",\n"
+ " \"fake_int\": \"42\",\n"
+ " \"fake_poll\": \"Hello \\\"world\\\"!\"\n"
+ " }\n"
+ "}\n",
+ eval_ctx_->DumpContext());
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/event_loop.cc b/update_manager/event_loop.cc
new file mode 100644
index 0000000..6ae6d36
--- /dev/null
+++ b/update_manager/event_loop.cc
@@ -0,0 +1,51 @@
+// 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/update_manager/event_loop.h"
+
+#include <cmath>
+
+using base::Closure;
+
+namespace {
+
+// Called by the GLib's main loop when is time to call the callback scheduled
+// with RunFromMainLopp() and similar functions. The pointer to the callback
+// passed when scheduling it is passed to this functions as a gpointer on
+// |user_data|.
+gboolean OnRanFromMainLoop(gpointer user_data) {
+ Closure* callback_p = reinterpret_cast<Closure*>(user_data);
+ callback_p->Run();
+ delete callback_p;
+ return FALSE; // Removes the source since a callback can only be called once.
+}
+
+} // namespace
+
+namespace chromeos_update_manager {
+
+EventId RunFromMainLoop(const Closure& callback) {
+ Closure* callback_p = new Closure(callback);
+ return g_idle_add_full(G_PRIORITY_DEFAULT,
+ OnRanFromMainLoop,
+ reinterpret_cast<gpointer>(callback_p),
+ NULL);
+}
+
+EventId RunFromMainLoopAfterTimeout(
+ const Closure& callback,
+ base::TimeDelta timeout) {
+ Closure* callback_p = new Closure(callback);
+ return g_timeout_add_seconds(static_cast<guint>(ceil(timeout.InSecondsF())),
+ OnRanFromMainLoop,
+ reinterpret_cast<gpointer>(callback_p));
+}
+
+bool CancelMainLoopEvent(EventId event) {
+ if (event != kEventIdNull)
+ return g_source_remove(event);
+ return false;
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/event_loop.h b/update_manager/event_loop.h
new file mode 100644
index 0000000..a24e0b7
--- /dev/null
+++ b/update_manager/event_loop.h
@@ -0,0 +1,37 @@
+// 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.
+
+// TODO(deymo): These functions interact with the glib's main loop. This should
+// be replaced by the libbase main loop once the process is migrated to that
+// main loop.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_EVENT_LOOP_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_EVENT_LOOP_H_
+
+#include <glib.h>
+
+#include <base/callback.h>
+#include <base/time/time.h>
+
+namespace chromeos_update_manager {
+
+typedef guint EventId;
+
+// A null EventId doesn't idenify any valid event.
+static constexpr EventId kEventIdNull = 0;
+
+// Schedules the passed |callback| to run from the GLib's main loop after a
+// timeout if it is given.
+EventId RunFromMainLoop(const base::Closure& callback);
+EventId RunFromMainLoopAfterTimeout(const base::Closure& callback,
+ base::TimeDelta timeout);
+
+// Removes the pending call |event| from the main loop. The value passed is the
+// one returned by the functions RunFromMainLoop*() when the call was scheduled.
+// Returns whether the event was found and removed.
+bool CancelMainLoopEvent(EventId event);
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_EVENT_LOOP_H_
diff --git a/update_manager/event_loop_unittest.cc b/update_manager/event_loop_unittest.cc
new file mode 100644
index 0000000..69325fe
--- /dev/null
+++ b/update_manager/event_loop_unittest.cc
@@ -0,0 +1,62 @@
+// 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/update_manager/event_loop.h"
+
+#include <base/bind.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/test_utils.h"
+
+using base::Bind;
+using base::TimeDelta;
+using chromeos_update_engine::RunGMainLoopMaxIterations;
+using chromeos_update_engine::RunGMainLoopUntil;
+
+namespace {
+
+// Sets the value of the passed pointer to true.
+void SetTrue(bool* value) {
+ *value = true;
+}
+
+bool GetBoolean(bool* value) {
+ return *value;
+}
+
+} // namespace
+
+namespace chromeos_update_manager {
+
+class EventLoopTest : public ::testing::Test {};
+
+TEST(EventLoopTest, RunFromMainLoopTest) {
+ bool called = false;
+ EventId ev = RunFromMainLoop(Bind(SetTrue, &called));
+ EXPECT_NE(0, ev);
+ RunGMainLoopMaxIterations(100);
+ EXPECT_TRUE(called);
+}
+
+// Tests that we can cancel events right after we schedule them.
+TEST(EventLoopTest, RunFromMainLoopCancelTest) {
+ bool called = false;
+ EventId ev = RunFromMainLoop(Bind(SetTrue, &called));
+ EXPECT_NE(0, ev);
+ EXPECT_TRUE(CancelMainLoopEvent(ev));
+ RunGMainLoopMaxIterations(100);
+ EXPECT_FALSE(called);
+}
+
+TEST(EventLoopTest, RunFromMainLoopAfterTimeoutTest) {
+ bool called = false;
+ EventId ev = RunFromMainLoopAfterTimeout(Bind(SetTrue, &called),
+ TimeDelta::FromSeconds(1));
+ EXPECT_NE(0, ev);
+ RunGMainLoopUntil(10000, Bind(GetBoolean, &called));
+ // Check that the main loop finished before the 10 seconds timeout.
+ EXPECT_TRUE(called);
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/fake_config_provider.h b/update_manager/fake_config_provider.h
new file mode 100644
index 0000000..84a1794
--- /dev/null
+++ b/update_manager/fake_config_provider.h
@@ -0,0 +1,32 @@
+// Copyright 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_CONFIG_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_CONFIG_PROVIDER_H_
+
+#include "update_engine/update_manager/config_provider.h"
+#include "update_engine/update_manager/fake_variable.h"
+
+namespace chromeos_update_manager {
+
+// Fake implementation of the ConfigProvider base class.
+class FakeConfigProvider : public ConfigProvider {
+ public:
+ FakeConfigProvider() {}
+
+ protected:
+ virtual FakeVariable<bool>* var_is_oobe_enabled() override {
+ return &var_is_oobe_enabled_;
+ }
+
+ private:
+ FakeVariable<bool> var_is_oobe_enabled_{
+ "is_oobe_enabled", kVariableModeConst};
+
+ DISALLOW_COPY_AND_ASSIGN(FakeConfigProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_CONFIG_PROVIDER_H_
diff --git a/update_manager/fake_device_policy_provider.h b/update_manager/fake_device_policy_provider.h
new file mode 100644
index 0000000..665d2bb
--- /dev/null
+++ b/update_manager/fake_device_policy_provider.h
@@ -0,0 +1,88 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_DEVICE_POLICY_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_DEVICE_POLICY_PROVIDER_H_
+
+#include <set>
+#include <string>
+
+#include "update_engine/update_manager/device_policy_provider.h"
+#include "update_engine/update_manager/fake_variable.h"
+
+namespace chromeos_update_manager {
+
+// Fake implementation of the DevicePolicyProvider base class.
+class FakeDevicePolicyProvider : public DevicePolicyProvider {
+ public:
+ FakeDevicePolicyProvider() {}
+
+ virtual FakeVariable<bool>* var_device_policy_is_loaded() override {
+ return &var_device_policy_is_loaded_;
+ }
+
+ virtual FakeVariable<std::string>* var_release_channel() override {
+ return &var_release_channel_;
+ }
+
+ virtual FakeVariable<bool>* var_release_channel_delegated() override {
+ return &var_release_channel_delegated_;
+ }
+
+ virtual FakeVariable<bool>* var_update_disabled() override {
+ return &var_update_disabled_;
+ }
+
+ virtual FakeVariable<std::string>* var_target_version_prefix() override {
+ return &var_target_version_prefix_;
+ }
+
+ virtual FakeVariable<base::TimeDelta>* var_scatter_factor() override {
+ return &var_scatter_factor_;
+ }
+
+ virtual FakeVariable<std::set<ConnectionType>>*
+ var_allowed_connection_types_for_update() override {
+ return &var_allowed_connection_types_for_update_;
+ }
+
+ virtual FakeVariable<std::string>* var_get_owner() override {
+ return &var_get_owner_;
+ }
+
+ virtual FakeVariable<bool>* var_http_downloads_enabled() override {
+ return &var_http_downloads_enabled_;
+ }
+
+ virtual FakeVariable<bool>* var_au_p2p_enabled() override {
+ return &var_au_p2p_enabled_;
+ }
+
+ private:
+ FakeVariable<bool> var_device_policy_is_loaded_{
+ "policy_is_loaded", kVariableModePoll};
+ FakeVariable<std::string> var_release_channel_{
+ "release_channel", kVariableModePoll};
+ FakeVariable<bool> var_release_channel_delegated_{
+ "release_channel_delegated", kVariableModePoll};
+ FakeVariable<bool> var_update_disabled_{
+ "update_disabled", kVariableModePoll};
+ FakeVariable<std::string> var_target_version_prefix_{
+ "target_version_prefix", kVariableModePoll};
+ FakeVariable<base::TimeDelta> var_scatter_factor_{
+ "scatter_factor", kVariableModePoll};
+ FakeVariable<std::set<ConnectionType>>
+ var_allowed_connection_types_for_update_{
+ "allowed_connection_types_for_update", kVariableModePoll};
+ FakeVariable<std::string> var_get_owner_{"get_owner", kVariableModePoll};
+ FakeVariable<bool> var_http_downloads_enabled_{
+ "http_downloads_enabled", kVariableModePoll};
+ FakeVariable<bool> var_au_p2p_enabled_{"au_p2p_enabled", kVariableModePoll};
+
+ DISALLOW_COPY_AND_ASSIGN(FakeDevicePolicyProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_DEVICE_POLICY_PROVIDER_H_
diff --git a/update_manager/fake_random_provider.h b/update_manager/fake_random_provider.h
new file mode 100644
index 0000000..2cf847c
--- /dev/null
+++ b/update_manager/fake_random_provider.h
@@ -0,0 +1,28 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_RANDOM_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_RANDOM_PROVIDER_H_
+
+#include "update_engine/update_manager/fake_variable.h"
+#include "update_engine/update_manager/random_provider.h"
+
+namespace chromeos_update_manager {
+
+// Fake implementation of the RandomProvider base class.
+class FakeRandomProvider : public RandomProvider {
+ public:
+ FakeRandomProvider() {}
+
+ virtual FakeVariable<uint64_t>* var_seed() override { return &var_seed_; }
+
+ private:
+ FakeVariable<uint64_t> var_seed_{"seed", kVariableModePoll};
+
+ DISALLOW_COPY_AND_ASSIGN(FakeRandomProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_RANDOM_PROVIDER_H_
diff --git a/update_manager/fake_shill_provider.h b/update_manager/fake_shill_provider.h
new file mode 100644
index 0000000..7528cc4
--- /dev/null
+++ b/update_manager/fake_shill_provider.h
@@ -0,0 +1,48 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_SHILL_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_SHILL_PROVIDER_H_
+
+#include "update_engine/update_manager/fake_variable.h"
+#include "update_engine/update_manager/shill_provider.h"
+
+namespace chromeos_update_manager {
+
+// Fake implementation of the ShillProvider base class.
+class FakeShillProvider : public ShillProvider {
+ public:
+ FakeShillProvider() {}
+
+ virtual FakeVariable<bool>* var_is_connected() override {
+ return &var_is_connected_;
+ }
+
+ virtual FakeVariable<ConnectionType>* var_conn_type() override {
+ return &var_conn_type_;
+ }
+
+ virtual FakeVariable<ConnectionTethering>*
+ var_conn_tethering() override {
+ return &var_conn_tethering_;
+ }
+
+ virtual FakeVariable<base::Time>* var_conn_last_changed() override {
+ return &var_conn_last_changed_;
+ }
+
+ private:
+ FakeVariable<bool> var_is_connected_{"is_connected", kVariableModePoll};
+ FakeVariable<ConnectionType> var_conn_type_{"conn_type", kVariableModePoll};
+ FakeVariable<ConnectionTethering> var_conn_tethering_{
+ "conn_tethering", kVariableModePoll};
+ FakeVariable<base::Time> var_conn_last_changed_{
+ "conn_last_changed", kVariableModePoll};
+
+ DISALLOW_COPY_AND_ASSIGN(FakeShillProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_SHILL_PROVIDER_H_
diff --git a/update_manager/fake_state.h b/update_manager/fake_state.h
new file mode 100644
index 0000000..2af8fe8
--- /dev/null
+++ b/update_manager/fake_state.h
@@ -0,0 +1,79 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_STATE_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_STATE_H_
+
+#include "update_engine/update_manager/fake_config_provider.h"
+#include "update_engine/update_manager/fake_device_policy_provider.h"
+#include "update_engine/update_manager/fake_random_provider.h"
+#include "update_engine/update_manager/fake_shill_provider.h"
+#include "update_engine/update_manager/fake_system_provider.h"
+#include "update_engine/update_manager/fake_time_provider.h"
+#include "update_engine/update_manager/fake_updater_provider.h"
+#include "update_engine/update_manager/state.h"
+
+namespace chromeos_update_manager {
+
+// A fake State class that creates fake providers for all the providers.
+// This fake can be used in unit testing of Policy subclasses. To fake out the
+// value a variable is exposing, just call FakeVariable<T>::SetValue() on the
+// variable you fake out. For example:
+//
+// FakeState fake_state_;
+// fake_state_.random_provider_->var_seed()->SetValue(new uint64_t(12345));
+//
+// You can call SetValue more than once and the FakeVariable will take care of
+// the memory, but only the last value will remain.
+class FakeState : public State {
+ public:
+ // Creates and initializes the FakeState using fake providers.
+ FakeState() {}
+
+ virtual ~FakeState() {}
+
+ // Downcasted detters to access the fake instances during testing.
+ virtual FakeConfigProvider* config_provider() override {
+ return &config_provider_;
+ }
+
+ virtual FakeDevicePolicyProvider* device_policy_provider() override {
+ return &device_policy_provider_;
+ }
+
+ virtual FakeRandomProvider* random_provider() override {
+ return &random_provider_;
+ }
+
+ virtual FakeShillProvider* shill_provider() override {
+ return &shill_provider_;
+ }
+
+ virtual FakeSystemProvider* system_provider() override {
+ return &system_provider_;
+ }
+
+ virtual FakeTimeProvider* time_provider() override {
+ return &time_provider_;
+ }
+
+ virtual FakeUpdaterProvider* updater_provider() override {
+ return &updater_provider_;
+ }
+
+ private:
+ FakeConfigProvider config_provider_;
+ FakeDevicePolicyProvider device_policy_provider_;
+ FakeRandomProvider random_provider_;
+ FakeShillProvider shill_provider_;
+ FakeSystemProvider system_provider_;
+ FakeTimeProvider time_provider_;
+ FakeUpdaterProvider updater_provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeState);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_STATE_H_
diff --git a/update_manager/fake_system_provider.h b/update_manager/fake_system_provider.h
new file mode 100644
index 0000000..6a6b474
--- /dev/null
+++ b/update_manager/fake_system_provider.h
@@ -0,0 +1,43 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_SYSTEM_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_SYSTEM_PROVIDER_H_
+
+#include "update_engine/update_manager/fake_variable.h"
+#include "update_engine/update_manager/system_provider.h"
+
+namespace chromeos_update_manager {
+
+// Fake implementation of the SystemProvider base class.
+class FakeSystemProvider : public SystemProvider {
+ public:
+ FakeSystemProvider() {}
+
+ virtual FakeVariable<bool>* var_is_normal_boot_mode() override {
+ return &var_is_normal_boot_mode_;
+ }
+
+ virtual FakeVariable<bool>* var_is_official_build() override {
+ return &var_is_official_build_;
+ }
+
+ virtual FakeVariable<bool>* var_is_oobe_complete() override {
+ return &var_is_oobe_complete_;
+ }
+
+ private:
+ FakeVariable<bool> var_is_normal_boot_mode_{
+ "is_normal_boot_mode", kVariableModeConst};
+ FakeVariable<bool> var_is_official_build_{
+ "is_official_build", kVariableModeConst};
+ FakeVariable<bool> var_is_oobe_complete_{
+ "is_oobe_complete", kVariableModePoll};
+
+ DISALLOW_COPY_AND_ASSIGN(FakeSystemProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_SYSTEM_PROVIDER_H_
diff --git a/update_manager/fake_time_provider.h b/update_manager/fake_time_provider.h
new file mode 100644
index 0000000..861b70a
--- /dev/null
+++ b/update_manager/fake_time_provider.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_TIME_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_TIME_PROVIDER_H_
+
+#include "update_engine/update_manager/fake_variable.h"
+#include "update_engine/update_manager/time_provider.h"
+
+namespace chromeos_update_manager {
+
+// Fake implementation of the TimeProvider base class.
+class FakeTimeProvider : public TimeProvider {
+ public:
+ FakeTimeProvider() {}
+
+ virtual FakeVariable<base::Time>* var_curr_date() override {
+ return &var_curr_date_;
+ }
+
+ virtual FakeVariable<int>* var_curr_hour() override {
+ return &var_curr_hour_;
+ }
+
+ private:
+ FakeVariable<base::Time> var_curr_date_{"curr_date", kVariableModePoll};
+ FakeVariable<int> var_curr_hour_{"curr_hour", kVariableModePoll};
+
+ DISALLOW_COPY_AND_ASSIGN(FakeTimeProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_TIME_PROVIDER_H_
diff --git a/update_manager/fake_update_manager.h b/update_manager/fake_update_manager.h
new file mode 100644
index 0000000..0831b2e
--- /dev/null
+++ b/update_manager/fake_update_manager.h
@@ -0,0 +1,36 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_UPDATE_MANAGER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_UPDATE_MANAGER_H_
+
+#include "update_engine/update_manager/update_manager.h"
+
+#include "update_engine/update_manager/default_policy.h"
+#include "update_engine/update_manager/fake_state.h"
+
+namespace chromeos_update_manager {
+
+class FakeUpdateManager : public UpdateManager {
+ public:
+ explicit FakeUpdateManager(chromeos_update_engine::ClockInterface* clock)
+ : UpdateManager(clock, new FakeState()) {
+ // The FakeUpdateManager uses a DefaultPolicy.
+ set_policy(new DefaultPolicy());
+ }
+
+ // UpdateManager overrides.
+ using UpdateManager::set_policy;
+
+ FakeState* state() {
+ return reinterpret_cast<FakeState*>(UpdateManager::state());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FakeUpdateManager);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_UPDATE_MANAGER_H_
diff --git a/update_manager/fake_updater_provider.h b/update_manager/fake_updater_provider.h
new file mode 100644
index 0000000..22cb9bf
--- /dev/null
+++ b/update_manager/fake_updater_provider.h
@@ -0,0 +1,94 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_UPDATER_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_UPDATER_PROVIDER_H_
+
+#include <string>
+
+#include "update_engine/update_manager/fake_variable.h"
+#include "update_engine/update_manager/updater_provider.h"
+
+namespace chromeos_update_manager {
+
+// Fake implementation of the UpdaterProvider base class.
+class FakeUpdaterProvider : public UpdaterProvider {
+ public:
+ FakeUpdaterProvider() {}
+
+ virtual FakeVariable<base::Time>* var_updater_started_time() override {
+ return &var_updater_started_time_;
+ }
+
+ virtual FakeVariable<base::Time>* var_last_checked_time() override {
+ return &var_last_checked_time_;
+ }
+
+ virtual FakeVariable<base::Time>* var_update_completed_time() override {
+ return &var_update_completed_time_;
+ }
+
+ virtual FakeVariable<double>* var_progress() override {
+ return &var_progress_;
+ }
+
+ virtual FakeVariable<Stage>* var_stage() override {
+ return &var_stage_;
+ }
+
+ virtual FakeVariable<std::string>* var_new_version() override {
+ return &var_new_version_;
+ }
+
+ virtual FakeVariable<int64_t>* var_payload_size() override {
+ return &var_payload_size_;
+ }
+
+ virtual FakeVariable<std::string>* var_curr_channel() override {
+ return &var_curr_channel_;
+ }
+
+ virtual FakeVariable<std::string>* var_new_channel() override {
+ return &var_new_channel_;
+ }
+
+ virtual FakeVariable<bool>* var_p2p_enabled() override {
+ return &var_p2p_enabled_;
+ }
+
+ virtual FakeVariable<bool>* var_cellular_enabled() override {
+ return &var_cellular_enabled_;
+ }
+
+ virtual FakeVariable<unsigned int>*
+ var_consecutive_failed_update_checks() override {
+ return &var_consecutive_failed_update_checks_;
+ }
+
+ private:
+ FakeVariable<base::Time> var_updater_started_time_{
+ "updater_started_time", kVariableModePoll};
+ FakeVariable<base::Time> var_last_checked_time_{
+ "last_checked_time", kVariableModePoll};
+ FakeVariable<base::Time> var_update_completed_time_{
+ "update_completed_time", kVariableModePoll};
+ FakeVariable<double> var_progress_{"progress", kVariableModePoll};
+ FakeVariable<Stage> var_stage_{"stage", kVariableModePoll};
+ FakeVariable<std::string> var_new_version_{"new_version", kVariableModePoll};
+ FakeVariable<int64_t> var_payload_size_{"payload_size", kVariableModePoll};
+ FakeVariable<std::string> var_curr_channel_{
+ "curr_channel", kVariableModePoll};
+ FakeVariable<std::string> var_new_channel_{"new_channel", kVariableModePoll};
+ FakeVariable<bool> var_p2p_enabled_{"p2p_enabled", kVariableModePoll};
+ FakeVariable<bool> var_cellular_enabled_{
+ "cellular_enabled", kVariableModePoll};
+ FakeVariable<unsigned int> var_consecutive_failed_update_checks_{
+ "consecutive_failed_update_checks", kVariableModePoll};
+
+ DISALLOW_COPY_AND_ASSIGN(FakeUpdaterProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_UPDATER_PROVIDER_H_
diff --git a/update_manager/fake_variable.h b/update_manager/fake_variable.h
new file mode 100644
index 0000000..0f976e3
--- /dev/null
+++ b/update_manager/fake_variable.h
@@ -0,0 +1,62 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_VARIABLE_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_VARIABLE_H_
+
+#include <string>
+
+#include <base/memory/scoped_ptr.h>
+
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+// A fake typed variable to use while testing policy implementations. The
+// variable can be instructed to return any object of its type.
+template<typename T>
+class FakeVariable : public Variable<T> {
+ public:
+ FakeVariable(const std::string& name, VariableMode mode)
+ : Variable<T>(name, mode) {}
+ FakeVariable(const std::string& name, base::TimeDelta poll_interval)
+ : Variable<T>(name, poll_interval) {}
+ virtual ~FakeVariable() {}
+
+ // Sets the next value of this variable to the passed |p_value| pointer. Once
+ // returned by GetValue(), the pointer is released and has to be set again.
+ // A value of NULL means that the GetValue() call will fail and return NULL.
+ void reset(const T* p_value) {
+ ptr_.reset(p_value);
+ }
+
+ // Make the NotifyValueChanged() public for FakeVariables.
+ void NotifyValueChanged() {
+ Variable<T>::NotifyValueChanged();
+ }
+
+ protected:
+ // Variable<T> overrides.
+ // Returns the pointer set with reset(). The ownership of the object is passed
+ // to the caller and the pointer is release from the FakeVariable. A second
+ // call to GetValue() without reset() will return NULL and set the error
+ // message.
+ virtual const T* GetValue(base::TimeDelta /* timeout */,
+ std::string* errmsg) {
+ if (ptr_ == NULL && errmsg != NULL)
+ *errmsg = this->GetName() + " is an empty FakeVariable";
+ // Passes the pointer ownership to the caller.
+ return ptr_.release();
+ }
+
+ private:
+ // The pointer returned by GetValue().
+ scoped_ptr<const T> ptr_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeVariable);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_FAKE_VARIABLE_H_
diff --git a/update_manager/generic_variables.h b/update_manager/generic_variables.h
new file mode 100644
index 0000000..5831463
--- /dev/null
+++ b/update_manager/generic_variables.h
@@ -0,0 +1,206 @@
+// 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.
+
+// Generic and provider-independent Variable subclasses. These variables can be
+// used by any state provider to implement simple variables to avoid repeat the
+// same common code on different state providers.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_GENERIC_VARIABLES_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_GENERIC_VARIABLES_H_
+
+#include <string>
+
+#include <base/callback.h>
+
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+// Variable class returning a copy of a given object using the copy constructor.
+// This template class can be used to define variables that expose as a variable
+// any fixed object, such as the a provider's private member. The variable will
+// create copies of the provided object using the copy constructor of that
+// class.
+//
+// For example, a state provider exposing a private member as a variable can
+// implement this as follows:
+//
+// class SomethingProvider {
+// public:
+// SomethingProvider(...) {
+// var_something_foo = new PollCopyVariable<MyType>(foo_);
+// }
+// ...
+// private:
+// MyType foo_;
+// };
+template<typename T>
+class PollCopyVariable : public Variable<T> {
+ public:
+ // Creates the variable returning copies of the passed |ref|. The reference to
+ // this object is kept and it should be available whenever the GetValue()
+ // method is called. If |is_set_p| is not null, then this flag will be
+ // consulted prior to returning the value, and an |errmsg| will be returned if
+ // it is not set.
+ PollCopyVariable(const std::string& name, const T& ref, const bool* is_set_p,
+ const std::string& errmsg)
+ : Variable<T>(name, kVariableModePoll), ref_(ref), is_set_p_(is_set_p),
+ errmsg_(errmsg) {}
+ PollCopyVariable(const std::string& name, const T& ref, const bool* is_set_p)
+ : PollCopyVariable(name, ref, is_set_p, std::string()) {}
+ PollCopyVariable(const std::string& name, const T& ref)
+ : PollCopyVariable(name, ref, nullptr) {}
+
+ PollCopyVariable(const std::string& name, const base::TimeDelta poll_interval,
+ const T& ref, const bool* is_set_p,
+ const std::string& errmsg)
+ : Variable<T>(name, poll_interval), ref_(ref), is_set_p_(is_set_p),
+ errmsg_(errmsg) {}
+ PollCopyVariable(const std::string& name, const base::TimeDelta poll_interval,
+ const T& ref, const bool* is_set_p)
+ : PollCopyVariable(name, poll_interval, ref, is_set_p, std::string()) {}
+ PollCopyVariable(const std::string& name, const base::TimeDelta poll_interval,
+ const T& ref)
+ : PollCopyVariable(name, poll_interval, ref, nullptr) {}
+
+ protected:
+ FRIEND_TEST(UmPollCopyVariableTest, SimpleTest);
+ FRIEND_TEST(UmPollCopyVariableTest, UseCopyConstructorTest);
+
+ // Variable override.
+ virtual inline const T* GetValue(base::TimeDelta /* timeout */,
+ std::string* errmsg) {
+ if (is_set_p_ && !(*is_set_p_)) {
+ if (errmsg) {
+ if (errmsg_.empty())
+ *errmsg = "No value set for " + this->GetName();
+ else
+ *errmsg = errmsg_;
+ }
+ return nullptr;
+ }
+ return new T(ref_);
+ }
+
+ private:
+ // Reference to the object to be copied by GetValue().
+ const T& ref_;
+
+ // A pointer to a flag indicating whether the value is set. If null, then the
+ // value is assumed to be set.
+ const bool* const is_set_p_;
+
+ // An error message to be returned when attempting to get an unset value.
+ const std::string errmsg_;
+};
+
+// Variable class returning a constant value that is cached on the variable when
+// it is created.
+template<typename T>
+class ConstCopyVariable : public Variable<T> {
+ public:
+ // Creates the variable returning copies of the passed |obj|. The value passed
+ // is copied in this variable, and new copies of it will be returned by
+ // GetValue().
+ ConstCopyVariable(const std::string& name, const T& obj)
+ : Variable<T>(name, kVariableModeConst), obj_(obj) {}
+
+ protected:
+ // Variable override.
+ virtual const T* GetValue(base::TimeDelta /* timeout */,
+ std::string* /* errmsg */) {
+ return new T(obj_);
+ }
+
+ private:
+ // Value to be copied by GetValue().
+ const T obj_;
+};
+
+// Variable class returning a copy of a value returned by a given function. The
+// function is called every time the variable is being polled.
+template<typename T>
+class CallCopyVariable : public Variable<T> {
+ public:
+ CallCopyVariable(const std::string& name, base::Callback<T(void)> func)
+ : Variable<T>(name, kVariableModePoll), func_(func) {}
+ CallCopyVariable(const std::string& name,
+ const base::TimeDelta poll_interval,
+ base::Callback<T(void)> func)
+ : Variable<T>(name, poll_interval), func_(func) {}
+
+ protected:
+ // Variable override.
+ virtual const T* GetValue(base::TimeDelta /* timeout */,
+ std::string* /* errmsg */) {
+ if (func_.is_null())
+ return nullptr;
+ return new T(func_.Run());
+ }
+
+ private:
+ FRIEND_TEST(UmCallCopyVariableTest, SimpleTest);
+
+ // The function to be called, stored as a base::Callback.
+ base::Callback<T(void)> func_;
+
+ DISALLOW_COPY_AND_ASSIGN(CallCopyVariable);
+};
+
+
+// A Variable class to implement simple Async variables. It provides two methods
+// SetValue and UnsetValue to modify the current value of the variable and
+// notify the registered observers whenever the value changed.
+//
+// The type T needs to be copy-constructable, default-constructable and have an
+// operator== (to determine if the value changed), which makes this class
+// suitable for basic types.
+template<typename T>
+class AsyncCopyVariable : public Variable<T> {
+ public:
+ explicit AsyncCopyVariable(const std::string& name)
+ : Variable<T>(name, kVariableModeAsync), has_value_(false) {}
+
+ AsyncCopyVariable(const std::string& name, const T value)
+ : Variable<T>(name, kVariableModeAsync),
+ has_value_(true), value_(value) {}
+
+ void SetValue(const T& new_value) {
+ bool should_notify = !(has_value_ && new_value == value_);
+ value_ = new_value;
+ has_value_ = true;
+ if (should_notify)
+ this->NotifyValueChanged();
+ }
+
+ void UnsetValue() {
+ if (has_value_) {
+ has_value_ = false;
+ this->NotifyValueChanged();
+ }
+ }
+
+ protected:
+ // Variable override.
+ virtual const T* GetValue(base::TimeDelta /* timeout */,
+ std::string* errmsg) {
+ if (!has_value_) {
+ if (errmsg)
+ *errmsg = "No value set for " + this->GetName();
+ return nullptr;
+ }
+ return new T(value_);
+ }
+
+ private:
+ // Whether the variable has a value set.
+ bool has_value_;
+
+ // Copy of the object to be returned by GetValue().
+ T value_;
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_GENERIC_VARIABLES_H_
diff --git a/update_manager/generic_variables_unittest.cc b/update_manager/generic_variables_unittest.cc
new file mode 100644
index 0000000..51ae4bd
--- /dev/null
+++ b/update_manager/generic_variables_unittest.cc
@@ -0,0 +1,201 @@
+// 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/update_manager/generic_variables.h"
+
+#include <base/callback.h>
+#include <base/memory/scoped_ptr.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/test_utils.h"
+#include "update_engine/update_manager/umtest_utils.h"
+
+using base::TimeDelta;
+using chromeos_update_engine::RunGMainLoopMaxIterations;
+
+namespace chromeos_update_manager {
+
+class UmPollCopyVariableTest : public ::testing::Test {};
+
+
+TEST_F(UmPollCopyVariableTest, SimpleTest) {
+ // Tests that copies are generated as intended.
+ int source = 5;
+ PollCopyVariable<int> var("var", source);
+
+ // Generate and validate a copy.
+ scoped_ptr<const int> copy_1(var.GetValue(
+ UmTestUtils::DefaultTimeout(), NULL));
+ UMTEST_ASSERT_NOT_NULL(copy_1.get());
+ EXPECT_EQ(5, *copy_1);
+
+ // Assign a different value to the source variable.
+ source = 42;
+
+ // Check that the content of the copy was not affected (distinct instance).
+ EXPECT_EQ(5, *copy_1);
+
+ // Generate and validate a second copy.
+ UmTestUtils::ExpectVariableHasValue(42, &var);
+}
+
+TEST_F(UmPollCopyVariableTest, SetFlagTest) {
+ // Tests that the set flag is being referred to as expected.
+ int source = 5;
+ bool is_set = false;
+ PollCopyVariable<int> var("var", source, &is_set);
+
+ // Flag marked unset, nothing should be returned.
+ UmTestUtils::ExpectVariableNotSet(&var);
+
+ // Flag marked set, we should be getting a value.
+ is_set = true;
+ UmTestUtils::ExpectVariableHasValue(5, &var);
+}
+
+
+class CopyConstructorTestClass {
+ public:
+ CopyConstructorTestClass(void) : copied_(false) {}
+ CopyConstructorTestClass(const CopyConstructorTestClass& other)
+ : copied_(true), val_(other.val_ * 2) {}
+
+ // Tells if the instance was constructed using the copy-constructor.
+ const bool copied_;
+
+ // An auxiliary internal value.
+ int val_ = 0;
+};
+
+
+TEST_F(UmPollCopyVariableTest, UseCopyConstructorTest) {
+ // Ensures that CopyVariables indeed uses the copy contructor.
+ const CopyConstructorTestClass source;
+ ASSERT_FALSE(source.copied_);
+
+ PollCopyVariable<CopyConstructorTestClass> var("var", source);
+ scoped_ptr<const CopyConstructorTestClass> copy(
+ var.GetValue(UmTestUtils::DefaultTimeout(), NULL));
+ UMTEST_ASSERT_NOT_NULL(copy.get());
+ EXPECT_TRUE(copy->copied_);
+}
+
+
+class UmConstCopyVariableTest : public ::testing::Test {};
+
+TEST_F(UmConstCopyVariableTest, SimpleTest) {
+ int source = 5;
+ ConstCopyVariable<int> var("var", source);
+ UmTestUtils::ExpectVariableHasValue(5, &var);
+
+ // Ensure the value is cached.
+ source = 42;
+ UmTestUtils::ExpectVariableHasValue(5, &var);
+}
+
+
+class UmCallCopyVariableTest : public ::testing::Test {};
+
+CopyConstructorTestClass test_func(CopyConstructorTestClass* obj) {
+ obj->val_++; // So we can check that the function was called.
+ return *obj;
+}
+
+TEST_F(UmCallCopyVariableTest, SimpleTest) {
+ // Tests that the returned value is generated by copying the value returned by
+ // the function call.
+
+ CopyConstructorTestClass test_obj;
+ ASSERT_FALSE(test_obj.copied_);
+ test_obj.val_ = 5;
+
+ base::Callback<CopyConstructorTestClass(void)> cb = base::Bind(
+ test_func, &test_obj);
+ CallCopyVariable<CopyConstructorTestClass> var("var", cb);
+
+ scoped_ptr<const CopyConstructorTestClass> copy(
+ var.GetValue(UmTestUtils::DefaultTimeout(), nullptr));
+ EXPECT_EQ(6, test_obj.val_); // Check that the function was called.
+ UMTEST_ASSERT_NOT_NULL(copy.get());
+ EXPECT_TRUE(copy->copied_);
+ EXPECT_EQ(12, copy->val_); // Check that copying occurred once.
+}
+
+TEST_F(UmCallCopyVariableTest, NullTest) {
+ // Ensures that the variable returns null when the callback is null.
+
+ base::Callback<bool(void)> cb;
+ CallCopyVariable<bool> var("var", cb);
+ UmTestUtils::ExpectVariableNotSet(&var);
+}
+
+class UmAsyncCopyVariableTest : public ::testing::Test {
+ public:
+ void TearDown() {
+ // No remaining event on the main loop.
+ EXPECT_EQ(0, RunGMainLoopMaxIterations(1));
+ }
+};
+
+TEST_F(UmAsyncCopyVariableTest, ConstructorTest) {
+ AsyncCopyVariable<int> var("var");
+ UmTestUtils::ExpectVariableNotSet(&var);
+ EXPECT_EQ(kVariableModeAsync, var.GetMode());
+}
+
+TEST_F(UmAsyncCopyVariableTest, SetValueTest) {
+ AsyncCopyVariable<int> var("var");
+ var.SetValue(5);
+ UmTestUtils::ExpectVariableHasValue(5, &var);
+ // Execute all the pending observers.
+ RunGMainLoopMaxIterations(100);
+}
+
+TEST_F(UmAsyncCopyVariableTest, UnsetValueTest) {
+ AsyncCopyVariable<int> var("var", 42);
+ var.UnsetValue();
+ UmTestUtils::ExpectVariableNotSet(&var);
+ // Execute all the pending observers.
+ RunGMainLoopMaxIterations(100);
+}
+
+class CallCounterObserver : public BaseVariable::ObserverInterface {
+ public:
+ void ValueChanged(BaseVariable* variable) {
+ calls_count_++;
+ }
+
+ int calls_count_ = 0;
+};
+
+TEST_F(UmAsyncCopyVariableTest, ObserverCalledTest) {
+ AsyncCopyVariable<int> var("var", 42);
+ CallCounterObserver observer;
+ var.AddObserver(&observer);
+ EXPECT_EQ(0, observer.calls_count_);
+
+ // Check that a different value fires the notification.
+ var.SetValue(5);
+ RunGMainLoopMaxIterations(100);
+ EXPECT_EQ(1, observer.calls_count_);
+
+ // Check the same value doesn't.
+ var.SetValue(5);
+ RunGMainLoopMaxIterations(100);
+ EXPECT_EQ(1, observer.calls_count_);
+
+ // Check that unsetting a previously set value fires the notification.
+ var.UnsetValue();
+ RunGMainLoopMaxIterations(100);
+ EXPECT_EQ(2, observer.calls_count_);
+
+ // Check that unsetting again doesn't.
+ var.UnsetValue();
+ RunGMainLoopMaxIterations(100);
+ EXPECT_EQ(2, observer.calls_count_);
+
+ var.RemoveObserver(&observer);
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/mock_policy.h b/update_manager/mock_policy.h
new file mode 100644
index 0000000..262b246
--- /dev/null
+++ b/update_manager/mock_policy.h
@@ -0,0 +1,40 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_MOCK_POLICY_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_MOCK_POLICY_H_
+
+#include <gmock/gmock.h>
+
+#include "update_engine/update_manager/policy.h"
+
+namespace chromeos_update_manager {
+
+// A mocked implementation of Policy.
+class MockPolicy : public Policy {
+ public:
+ MockPolicy() {}
+ virtual ~MockPolicy() {}
+
+ // Policy overrides.
+ MOCK_CONST_METHOD4(UpdateCheckAllowed,
+ EvalStatus(EvaluationContext*, State*, std::string*,
+ UpdateCheckParams*));
+
+ MOCK_CONST_METHOD6(UpdateCanStart,
+ EvalStatus(EvaluationContext*, State*, std::string*,
+ UpdateCanStartResult*,
+ const bool, const UpdateState&));
+
+ MOCK_CONST_METHOD4(UpdateCanStart,
+ EvalStatus(EvaluationContext*, State*, std::string*,
+ bool*));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockPolicy);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_MOCK_POLICY_H_
diff --git a/update_manager/mock_variable.h b/update_manager/mock_variable.h
new file mode 100644
index 0000000..c2ba375
--- /dev/null
+++ b/update_manager/mock_variable.h
@@ -0,0 +1,28 @@
+// Copyright 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_MOCK_VARIABLE_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_MOCK_VARIABLE_H_
+
+#include <gmock/gmock.h>
+
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+// This is a generic mock of the Variable class.
+template<typename T>
+class MockVariable : public Variable<T> {
+ public:
+ using Variable<T>::Variable;
+
+ MOCK_METHOD2_T(GetValue, const T*(base::TimeDelta, std::string*));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockVariable);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_MOCK_VARIABLE_H_
diff --git a/update_manager/policy.cc b/update_manager/policy.cc
new file mode 100644
index 0000000..41ff8c1
--- /dev/null
+++ b/update_manager/policy.cc
@@ -0,0 +1,25 @@
+// 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/update_manager/policy.h"
+
+#include <string>
+
+using std::string;
+
+namespace chromeos_update_manager {
+
+string ToString(EvalStatus status) {
+ switch (status) {
+ case EvalStatus::kFailed:
+ return "kFailed";
+ case EvalStatus::kSucceeded:
+ return "kSucceeded";
+ case EvalStatus::kAskMeAgainLater:
+ return "kAskMeAgainLater";
+ }
+ return "Invalid";
+}
+
+} // namespace chromeos_update_engine
diff --git a/update_manager/policy.h b/update_manager/policy.h
new file mode 100644
index 0000000..65f236c
--- /dev/null
+++ b/update_manager/policy.h
@@ -0,0 +1,137 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_POLICY_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_POLICY_H_
+
+#include <string>
+
+#include "update_engine/update_manager/evaluation_context.h"
+#include "update_engine/update_manager/state.h"
+
+namespace chromeos_update_manager {
+
+// The three different results of a policy request.
+enum class EvalStatus {
+ kFailed,
+ kSucceeded,
+ kAskMeAgainLater,
+};
+
+std::string ToString(EvalStatus status);
+
+// Parameters of an update check. These parameters are determined by the
+// UpdateCheckAllowed policy.
+struct UpdateCheckParams {
+ bool updates_enabled; // Whether the auto-updates are enabled on this build.
+};
+
+// Input arguments to UpdateCanStart.
+//
+// A snapshot of the state of the current update process.
+struct UpdateState {
+ // Time when update was first offered by Omaha.
+ base::Time first_seen;
+ // Number of update checks returning the current update.
+ int num_checks;
+ // Scattering wallclock-based wait period, as returned by the policy.
+ base::TimeDelta scatter_wait_period;
+ // Maximum wait period allowed for this update, as determined by Omaha.
+ base::TimeDelta scatter_wait_period_max;
+ // Scattering update check threshold, as returned by the policy.
+ int scatter_check_threshold;
+ // Minimum/maximum check threshold values.
+ // TODO(garnold) These appear to not be related to the current update and so
+ // should probably be obtained as variables via UpdaterProvider.
+ int scatter_check_threshold_min;
+ int scatter_check_threshold_max;
+};
+
+// Results regarding the downloading and applying of an update, as determined by
+// UpdateCanStart.
+//
+// An enumerator for the reasons of not allowing an update to start.
+enum class UpdateCannotStartReason {
+ kUndefined,
+ kCheckDue,
+ kDisabledByPolicy,
+ kScattering,
+};
+
+struct UpdateCanStartResult {
+ // Whether the update attempt is allowed to proceed.
+ bool update_can_start;
+ // Attributes pertaining to the case where update is allowed. The update
+ // engine uses them to choose the means for downloading and applying an
+ // update.
+ bool http_allowed;
+ bool p2p_allowed;
+ std::string target_channel;
+ // Attributes pertaining to the case where update is not allowed. Some are
+ // needed for storing values to persistent storage, others for
+ // logging/metrics.
+ UpdateCannotStartReason cannot_start_reason;
+ base::TimeDelta scatter_wait_period; // Needs to be persisted.
+ int scatter_check_threshold; // Needs to be persisted.
+};
+
+// The Policy class is an interface to the ensemble of policy requests that the
+// client can make. A derived class includes the policy implementations of
+// these.
+//
+// When compile-time selection of the policy is required due to missing or extra
+// parts in a given platform, a different Policy subclass can be used.
+class Policy {
+ public:
+ virtual ~Policy() {}
+
+ // List of policy requests. A policy request takes an EvaluationContext as the
+ // first argument, a State instance, a returned error message, a returned
+ // value and optionally followed by one or more arbitrary constant arguments.
+ //
+ // When the implementation fails, the method returns EvalStatus::kFailed and
+ // sets the |error| string.
+
+ // UpdateCheckAllowed returns whether it is allowed to request an update check
+ // to Omaha.
+ virtual EvalStatus UpdateCheckAllowed(
+ EvaluationContext* ec, State* state, std::string* error,
+ UpdateCheckParams* result) const = 0;
+
+ // Returns EvalStatus::kSucceeded if either an update can start being
+ // processed, or the attempt needs to be aborted. In cases where the update
+ // needs to wait for some condition to be satisfied, but none of the values
+ // that need to be persisted has changed, returns
+ // EvalStatus::kAskMeAgainLater. Arguments include an |interactive| flag that
+ // tells whether the update is user initiated, and an |update_state| that
+ // encapsulates data pertaining to the currnet ongoing update process.
+ virtual EvalStatus UpdateCanStart(
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ UpdateCanStartResult* result,
+ const bool interactive,
+ const UpdateState& update_state) const = 0;
+
+ // Checks whether updating is allowed over the current network connection
+ // Consults the shill provider as well as the device policy (if available).
+ // Returns |EvalStatus::kSucceeded|, setting |result| according to whether or
+ // not the current connection can be used; on failure, returns
+ // |EvalStatus::kFailed| and sets |error| accordingly.
+ virtual EvalStatus UpdateCurrentConnectionAllowed(
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ bool* result) const = 0;
+
+ protected:
+ Policy() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Policy);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_POLICY_H_
diff --git a/update_manager/policy_utils.h b/update_manager/policy_utils.h
new file mode 100644
index 0000000..9e60167
--- /dev/null
+++ b/update_manager/policy_utils.h
@@ -0,0 +1,26 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_POLICY_UTILS_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_POLICY_UTILS_H_
+
+#include "update_engine/update_manager/policy.h"
+
+// Checks that the passed pointer value is not null, returning kFailed on the
+// current context and setting the *error description when it is null. The
+// intended use is to validate variable failures while using
+// EvaluationContext::GetValue, for example:
+//
+// const int* my_value = ec->GetValue(state->my_provider()->var_my_value());
+// POLICY_CHECK_VALUE_AND_FAIL(my_value, error);
+//
+#define POLICY_CHECK_VALUE_AND_FAIL(ptr, error) \
+ do { \
+ if ((ptr) == nullptr) { \
+ *(error) = #ptr " is required but is null."; \
+ return EvalStatus::kFailed; \
+ } \
+ } while (false)
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_POLICY_UTILS_H_
diff --git a/update_manager/prng.h b/update_manager/prng.h
new file mode 100644
index 0000000..ca07e5e
--- /dev/null
+++ b/update_manager/prng.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_PRNG_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_PRNG_H_
+
+#include <random>
+
+#include <base/logging.h>
+
+namespace chromeos_update_manager {
+
+// A thread-safe, unsecure, 32-bit pseudo-random number generator based on
+// std::mt19937.
+class PRNG {
+ public:
+ // Initializes the generator with the passed |seed| value.
+ explicit PRNG(uint32_t seed) : gen_(seed) {}
+
+ // Returns a random unsigned 32-bit integer.
+ uint32_t Rand() { return gen_(); }
+
+ // Returns a random integer uniformly distributed in the range [min, max].
+ int RandMinMax(int min, int max) {
+ DCHECK_LE(min, max);
+ return std::uniform_int_distribution<>(min, max)(gen_);
+ }
+
+ private:
+ // A pseudo-random number generator.
+ std::mt19937 gen_;
+
+ DISALLOW_COPY_AND_ASSIGN(PRNG);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_PRNG_H_
diff --git a/update_manager/prng_unittest.cc b/update_manager/prng_unittest.cc
new file mode 100644
index 0000000..4d165b3
--- /dev/null
+++ b/update_manager/prng_unittest.cc
@@ -0,0 +1,67 @@
+// 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/update_manager/prng.h"
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+using std::vector;
+
+namespace chromeos_update_manager {
+
+TEST(UmPRNGTest, ShouldBeDeterministic) {
+ PRNG a(42);
+ PRNG b(42);
+
+ for (int i = 0; i < 1000; ++i) {
+ EXPECT_EQ(a.Rand(), b.Rand()) << "Iteration i=" << i;
+ }
+}
+
+TEST(UmPRNGTest, SeedChangesGeneratedSequence) {
+ PRNG a(42);
+ PRNG b(5);
+
+ vector<uint32_t> values_a;
+ vector<uint32_t> values_b;
+
+ for (int i = 0; i < 100; ++i) {
+ values_a.push_back(a.Rand());
+ values_b.push_back(b.Rand());
+ }
+ EXPECT_NE(values_a, values_b);
+}
+
+TEST(UmPRNGTest, IsNotConstant) {
+ PRNG prng(5);
+
+ uint32_t initial_value = prng.Rand();
+ bool prng_is_constant = true;
+ for (int i = 0; i < 100; ++i) {
+ if (prng.Rand() != initial_value) {
+ prng_is_constant = false;
+ break;
+ }
+ }
+ EXPECT_FALSE(prng_is_constant) << "After 100 iterations.";
+}
+
+TEST(UmPRNGTest, RandCoversRange) {
+ PRNG a(42);
+ int hits[11] = { 0 };
+
+ for (int i = 0; i < 1000; i++) {
+ int r = a.RandMinMax(0, 10);
+ ASSERT_LE(0, r);
+ ASSERT_GE(10, r);
+ hits[r]++;
+ }
+
+ for (auto& hit : hits)
+ EXPECT_LT(0, hit);
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/provider.h b/update_manager/provider.h
new file mode 100644
index 0000000..3fb1d90
--- /dev/null
+++ b/update_manager/provider.h
@@ -0,0 +1,26 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_PROVIDER_H_
+
+#include <base/basictypes.h>
+
+namespace chromeos_update_manager {
+
+// Abstract base class for a policy provider.
+class Provider {
+ public:
+ virtual ~Provider() {}
+
+ protected:
+ Provider() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Provider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_PROVIDER_H_
diff --git a/update_manager/random_provider.h b/update_manager/random_provider.h
new file mode 100644
index 0000000..dede41c
--- /dev/null
+++ b/update_manager/random_provider.h
@@ -0,0 +1,33 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_RANDOM_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_RANDOM_PROVIDER_H_
+
+#include "update_engine/update_manager/provider.h"
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+// Provider of random values.
+class RandomProvider : public Provider {
+ public:
+ virtual ~RandomProvider() {}
+
+ // Return a random number every time it is requested. Note that values
+ // returned by the variables are cached by the EvaluationContext, so the
+ // returned value will be the same during the same policy request. If more
+ // random values are needed use a PRNG seeded with this value.
+ virtual Variable<uint64_t>* var_seed() = 0;
+
+ protected:
+ RandomProvider() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RandomProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_RANDOM_PROVIDER_H_
diff --git a/update_manager/real_config_provider.cc b/update_manager/real_config_provider.cc
new file mode 100644
index 0000000..0e76b73
--- /dev/null
+++ b/update_manager/real_config_provider.cc
@@ -0,0 +1,51 @@
+// Copyright 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/update_manager/real_config_provider.h"
+
+#include <base/logging.h>
+
+#include "update_engine/constants.h"
+#include "update_engine/simple_key_value_store.h"
+#include "update_engine/update_manager/generic_variables.h"
+#include "update_engine/utils.h"
+
+using chromeos_update_engine::KeyValueStore;
+using std::string;
+
+namespace {
+
+const char* kConfigFilePath = "/etc/update_manager.conf";
+
+// Config options:
+const char* kConfigOptsIsOOBEEnabled = "is_oobe_enabled";
+
+} // namespace
+
+namespace chromeos_update_manager {
+
+bool RealConfigProvider::Init() {
+ KeyValueStore store;
+
+ if (hardware_->IsNormalBootMode()) {
+ store.Load(root_prefix_ + kConfigFilePath);
+ } else {
+ if (store.Load(root_prefix_ + chromeos_update_engine::kStatefulPartition +
+ kConfigFilePath)) {
+ LOG(INFO) << "UpdateManager Config loaded from stateful partition.";
+ } else {
+ store.Load(root_prefix_ + kConfigFilePath);
+ }
+ }
+
+ bool is_oobe_enabled;
+ if (!store.GetBoolean(kConfigOptsIsOOBEEnabled, &is_oobe_enabled))
+ is_oobe_enabled = true; // Default value.
+ var_is_oobe_enabled_.reset(
+ new ConstCopyVariable<bool>(kConfigOptsIsOOBEEnabled, is_oobe_enabled));
+
+ return true;
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/real_config_provider.h b/update_manager/real_config_provider.h
new file mode 100644
index 0000000..e525e38
--- /dev/null
+++ b/update_manager/real_config_provider.h
@@ -0,0 +1,54 @@
+// Copyright 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_CONFIG_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_CONFIG_PROVIDER_H_
+
+#include <string>
+
+#include <base/memory/scoped_ptr.h>
+
+#include "update_engine/hardware_interface.h"
+#include "update_engine/update_manager/config_provider.h"
+#include "update_engine/update_manager/generic_variables.h"
+
+namespace chromeos_update_manager {
+
+// ConfigProvider concrete implementation.
+class RealConfigProvider : public ConfigProvider {
+ public:
+ explicit RealConfigProvider(
+ chromeos_update_engine::HardwareInterface* hardware)
+ : hardware_(hardware) {}
+
+ // Initializes the provider and returns whether it succeeded.
+ bool Init();
+
+ Variable<bool>* var_is_oobe_enabled() override {
+ return var_is_oobe_enabled_.get();
+ }
+
+ private:
+ friend class UmRealConfigProviderTest;
+
+ // Used for testing. Sets the root prefix, which is by default "". Call this
+ // method before calling Init() in order to mock out the place where the files
+ // are being read from.
+ void SetRootPrefix(const std::string& prefix) {
+ root_prefix_ = prefix;
+ }
+
+ scoped_ptr<ConstCopyVariable<bool>> var_is_oobe_enabled_;
+
+ chromeos_update_engine::HardwareInterface* hardware_;
+
+ // Prefix to prepend to the file paths. Useful for testing.
+ std::string root_prefix_;
+
+ DISALLOW_COPY_AND_ASSIGN(RealConfigProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_CONFIG_PROVIDER_H_
diff --git a/update_manager/real_config_provider_unittest.cc b/update_manager/real_config_provider_unittest.cc
new file mode 100644
index 0000000..fc20e36
--- /dev/null
+++ b/update_manager/real_config_provider_unittest.cc
@@ -0,0 +1,88 @@
+// Copyright 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/update_manager/real_config_provider.h"
+
+#include <base/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/memory/scoped_ptr.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/constants.h"
+#include "update_engine/fake_hardware.h"
+#include "update_engine/test_utils.h"
+#include "update_engine/update_manager/umtest_utils.h"
+
+using base::TimeDelta;
+using chromeos_update_engine::WriteFileString;
+using std::string;
+
+namespace chromeos_update_manager {
+
+class UmRealConfigProviderTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ ASSERT_TRUE(root_dir_.CreateUniqueTempDir());
+ provider_.reset(new RealConfigProvider(&fake_hardware_));
+ provider_->SetRootPrefix(root_dir_.path().value());
+ }
+
+ void WriteStatefulConfig(const string& config) {
+ base::FilePath kFile(root_dir_.path().value()
+ + chromeos_update_engine::kStatefulPartition
+ + "/etc/update_manager.conf");
+ ASSERT_TRUE(base::CreateDirectory(kFile.DirName()));
+ ASSERT_TRUE(WriteFileString(kFile.value(), config));
+ }
+
+ void WriteRootfsConfig(const string& config) {
+ base::FilePath kFile(root_dir_.path().value()
+ + "/etc/update_manager.conf");
+ ASSERT_TRUE(base::CreateDirectory(kFile.DirName()));
+ ASSERT_TRUE(WriteFileString(kFile.value(), config));
+ }
+
+ scoped_ptr<RealConfigProvider> provider_;
+ chromeos_update_engine::FakeHardware fake_hardware_;
+ TimeDelta default_timeout_ = TimeDelta::FromSeconds(1);
+ base::ScopedTempDir root_dir_;
+};
+
+TEST_F(UmRealConfigProviderTest, InitTest) {
+ EXPECT_TRUE(provider_->Init());
+ UMTEST_EXPECT_NOT_NULL(provider_->var_is_oobe_enabled());
+}
+
+TEST_F(UmRealConfigProviderTest, NoFileFoundReturnsDefault) {
+ EXPECT_TRUE(provider_->Init());
+ UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_oobe_enabled());
+}
+
+TEST_F(UmRealConfigProviderTest, DontReadStatefulInNormalMode) {
+ fake_hardware_.SetIsNormalBootMode(true);
+ WriteStatefulConfig("is_oobe_enabled=false");
+
+ EXPECT_TRUE(provider_->Init());
+ UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_oobe_enabled());
+}
+
+TEST_F(UmRealConfigProviderTest, ReadStatefulInDevMode) {
+ fake_hardware_.SetIsNormalBootMode(false);
+ WriteRootfsConfig("is_oobe_enabled=true");
+ // Since the stateful is present, this should read that one.
+ WriteStatefulConfig("is_oobe_enabled=false");
+
+ EXPECT_TRUE(provider_->Init());
+ UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_oobe_enabled());
+}
+
+TEST_F(UmRealConfigProviderTest, ReadRootfsIfStatefulNotFound) {
+ fake_hardware_.SetIsNormalBootMode(false);
+ WriteRootfsConfig("is_oobe_enabled=false");
+
+ EXPECT_TRUE(provider_->Init());
+ UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_oobe_enabled());
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/real_device_policy_provider.cc b/update_manager/real_device_policy_provider.cc
new file mode 100644
index 0000000..e26ffe0
--- /dev/null
+++ b/update_manager/real_device_policy_provider.cc
@@ -0,0 +1,136 @@
+// 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/update_manager/real_device_policy_provider.h"
+
+#include <base/logging.h>
+#include <base/time/time.h>
+#include <policy/device_policy.h>
+
+#include "update_engine/update_manager/generic_variables.h"
+#include "update_engine/update_manager/real_shill_provider.h"
+#include "update_engine/utils.h"
+
+using base::TimeDelta;
+using policy::DevicePolicy;
+using std::set;
+using std::string;
+
+namespace {
+
+const int kDevicePolicyRefreshRateInMinutes = 60;
+
+} // namespace
+
+namespace chromeos_update_manager {
+
+RealDevicePolicyProvider::~RealDevicePolicyProvider() {
+ CancelMainLoopEvent(scheduled_refresh_);
+}
+
+bool RealDevicePolicyProvider::Init() {
+ CHECK(policy_provider_ != nullptr);
+
+ // On Init() we try to get the device policy and keep updating it.
+ RefreshDevicePolicyAndReschedule();
+
+ return true;
+}
+
+void RealDevicePolicyProvider::RefreshDevicePolicyAndReschedule() {
+ RefreshDevicePolicy();
+ scheduled_refresh_ = RunFromMainLoopAfterTimeout(
+ base::Bind(&RealDevicePolicyProvider::RefreshDevicePolicyAndReschedule,
+ base::Unretained(this)),
+ TimeDelta::FromMinutes(kDevicePolicyRefreshRateInMinutes));
+}
+
+template<typename T>
+void RealDevicePolicyProvider::UpdateVariable(
+ AsyncCopyVariable<T>* var,
+ bool (policy::DevicePolicy::*getter_method)(T*) const) {
+ T new_value;
+ if (policy_provider_->device_policy_is_loaded() &&
+ (policy_provider_->GetDevicePolicy().*getter_method)(&new_value)) {
+ var->SetValue(new_value);
+ } else {
+ var->UnsetValue();
+ }
+}
+
+template<typename T>
+void RealDevicePolicyProvider::UpdateVariable(
+ AsyncCopyVariable<T>* var,
+ bool (RealDevicePolicyProvider::*getter_method)(T*) const) {
+ T new_value;
+ if (policy_provider_->device_policy_is_loaded() &&
+ (this->*getter_method)(&new_value)) {
+ var->SetValue(new_value);
+ } else {
+ var->UnsetValue();
+ }
+}
+
+bool RealDevicePolicyProvider::ConvertAllowedConnectionTypesForUpdate(
+ set<ConnectionType>* allowed_types) const {
+ set<string> allowed_types_str;
+ if (!policy_provider_->GetDevicePolicy()
+ .GetAllowedConnectionTypesForUpdate(&allowed_types_str)) {
+ return false;
+ }
+ allowed_types->clear();
+ for (auto& type_str : allowed_types_str) {
+ ConnectionType type =
+ RealShillProvider::ParseConnectionType(type_str.c_str());
+ if (type != ConnectionType::kUnknown) {
+ allowed_types->insert(type);
+ } else {
+ LOG(WARNING) << "Policy includes unknown connection type: " << type_str;
+ }
+ }
+ return true;
+}
+
+bool RealDevicePolicyProvider::ConvertScatterFactor(
+ base::TimeDelta* scatter_factor) const {
+ int64 scatter_factor_in_seconds;
+ if (!policy_provider_->GetDevicePolicy().GetScatterFactorInSeconds(
+ &scatter_factor_in_seconds)) {
+ return false;
+ }
+ if (scatter_factor_in_seconds < 0) {
+ LOG(WARNING) << "Ignoring negative scatter factor: "
+ << scatter_factor_in_seconds;
+ return false;
+ }
+ *scatter_factor = base::TimeDelta::FromSeconds(scatter_factor_in_seconds);
+ return true;
+}
+
+void RealDevicePolicyProvider::RefreshDevicePolicy() {
+ if (!policy_provider_->Reload()) {
+ LOG(INFO) << "No device policies/settings present.";
+ }
+
+ var_device_policy_is_loaded_.SetValue(
+ policy_provider_->device_policy_is_loaded());
+
+ UpdateVariable(&var_release_channel_, &DevicePolicy::GetReleaseChannel);
+ UpdateVariable(&var_release_channel_delegated_,
+ &DevicePolicy::GetReleaseChannelDelegated);
+ UpdateVariable(&var_update_disabled_, &DevicePolicy::GetUpdateDisabled);
+ UpdateVariable(&var_target_version_prefix_,
+ &DevicePolicy::GetTargetVersionPrefix);
+ UpdateVariable(&var_scatter_factor_,
+ &RealDevicePolicyProvider::ConvertScatterFactor);
+ UpdateVariable(
+ &var_allowed_connection_types_for_update_,
+ &RealDevicePolicyProvider::ConvertAllowedConnectionTypesForUpdate);
+ UpdateVariable(&var_get_owner_, &DevicePolicy::GetOwner);
+ UpdateVariable(&var_http_downloads_enabled_,
+ &DevicePolicy::GetHttpDownloadsEnabled);
+ UpdateVariable(&var_au_p2p_enabled_, &DevicePolicy::GetAuP2PEnabled);
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/real_device_policy_provider.h b/update_manager/real_device_policy_provider.h
new file mode 100644
index 0000000..fdbfcd9
--- /dev/null
+++ b/update_manager/real_device_policy_provider.h
@@ -0,0 +1,135 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_DEVICE_POLICY_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_DEVICE_POLICY_PROVIDER_H_
+
+#include <set>
+#include <string>
+
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+#include <policy/libpolicy.h>
+
+#include "update_engine/update_manager/device_policy_provider.h"
+#include "update_engine/update_manager/event_loop.h"
+#include "update_engine/update_manager/generic_variables.h"
+
+namespace chromeos_update_manager {
+
+// DevicePolicyProvider concrete implementation.
+class RealDevicePolicyProvider : public DevicePolicyProvider {
+ public:
+ explicit RealDevicePolicyProvider(policy::PolicyProvider* policy_provider)
+ : policy_provider_(policy_provider) {}
+ ~RealDevicePolicyProvider();
+
+ // Initializes the provider and returns whether it succeeded.
+ bool Init();
+
+ virtual Variable<bool>* var_device_policy_is_loaded() override {
+ return &var_device_policy_is_loaded_;
+ }
+
+ virtual Variable<std::string>* var_release_channel() override {
+ return &var_release_channel_;
+ }
+
+ virtual Variable<bool>* var_release_channel_delegated() override {
+ return &var_release_channel_delegated_;
+ }
+
+ virtual Variable<bool>* var_update_disabled() override {
+ return &var_update_disabled_;
+ }
+
+ virtual Variable<std::string>* var_target_version_prefix() override {
+ return &var_target_version_prefix_;
+ }
+
+ virtual Variable<base::TimeDelta>* var_scatter_factor() override {
+ return &var_scatter_factor_;
+ }
+
+ virtual Variable<std::set<ConnectionType>>*
+ var_allowed_connection_types_for_update() override {
+ return &var_allowed_connection_types_for_update_;
+ }
+
+ virtual Variable<std::string>* var_get_owner() override {
+ return &var_get_owner_;
+ }
+
+ virtual Variable<bool>* var_http_downloads_enabled() override {
+ return &var_http_downloads_enabled_;
+ }
+
+ virtual Variable<bool>* var_au_p2p_enabled() override {
+ return &var_au_p2p_enabled_;
+ }
+
+ private:
+ FRIEND_TEST(UmRealDevicePolicyProviderTest, RefreshScheduledTest);
+ FRIEND_TEST(UmRealDevicePolicyProviderTest, NonExistentDevicePolicyReloaded);
+ FRIEND_TEST(UmRealDevicePolicyProviderTest, ValuesUpdated);
+
+ // Schedules a call to periodically refresh the device policy.
+ void RefreshDevicePolicyAndReschedule();
+
+ // Reloads the device policy and updates all the exposed variables.
+ void RefreshDevicePolicy();
+
+ // Updates the async variable |var| based on the result value of the method
+ // passed, which is a DevicePolicy getter method.
+ template<typename T>
+ void UpdateVariable(AsyncCopyVariable<T>* var,
+ bool (policy::DevicePolicy::*getter_method)(T*) const);
+
+ // Updates the async variable |var| based on the result value of the getter
+ // method passed, which is a wrapper getter on this class.
+ template<typename T>
+ void UpdateVariable(
+ AsyncCopyVariable<T>* var,
+ bool (RealDevicePolicyProvider::*getter_method)(T*) const);
+
+ // Wrapper for DevicePolicy::GetScatterFactorInSeconds() that converts the
+ // result to a base::TimeDelta. It returns the same value as
+ // GetScatterFactorInSeconds().
+ bool ConvertScatterFactor(base::TimeDelta* scatter_factor) const;
+
+ // Wrapper for DevicePolicy::GetAllowedConnectionTypesForUpdate() that
+ // converts the result to a set of ConnectionType elements instead of strings.
+ bool ConvertAllowedConnectionTypesForUpdate(
+ std::set<ConnectionType>* allowed_types) const;
+
+ // Used for fetching information about the device policy.
+ policy::PolicyProvider* policy_provider_;
+
+ // Used to schedule refreshes of the device policy.
+ EventId scheduled_refresh_ = kEventIdNull;
+
+ // Variable exposing whether the policy is loaded.
+ AsyncCopyVariable<bool> var_device_policy_is_loaded_{
+ "policy_is_loaded", false};
+
+ // Variables mapping the exposed methods from the policy::DevicePolicy.
+ AsyncCopyVariable<std::string> var_release_channel_{"release_channel"};
+ AsyncCopyVariable<bool> var_release_channel_delegated_{
+ "release_channel_delegated"};
+ AsyncCopyVariable<bool> var_update_disabled_{"update_disabled"};
+ AsyncCopyVariable<std::string> var_target_version_prefix_{
+ "target_version_prefix"};
+ AsyncCopyVariable<base::TimeDelta> var_scatter_factor_{"scatter_factor"};
+ AsyncCopyVariable<std::set<ConnectionType>>
+ var_allowed_connection_types_for_update_{
+ "allowed_connection_types_for_update"};
+ AsyncCopyVariable<std::string> var_get_owner_{"get_owner"};
+ AsyncCopyVariable<bool> var_http_downloads_enabled_{"http_downloads_enabled"};
+ AsyncCopyVariable<bool> var_au_p2p_enabled_{"au_p2p_enabled"};
+
+ DISALLOW_COPY_AND_ASSIGN(RealDevicePolicyProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_DEVICE_POLICY_PROVIDER_H_
diff --git a/update_manager/real_device_policy_provider_unittest.cc b/update_manager/real_device_policy_provider_unittest.cc
new file mode 100644
index 0000000..3b3d3e9
--- /dev/null
+++ b/update_manager/real_device_policy_provider_unittest.cc
@@ -0,0 +1,165 @@
+// 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/update_manager/real_device_policy_provider.h"
+
+#include <base/memory/scoped_ptr.h>
+#include <gtest/gtest.h>
+#include <policy/mock_device_policy.h>
+#include <policy/mock_libpolicy.h>
+
+#include "update_engine/test_utils.h"
+#include "update_engine/update_manager/umtest_utils.h"
+
+using base::TimeDelta;
+using chromeos_update_engine::RunGMainLoopMaxIterations;
+using std::set;
+using std::string;
+using testing::AtLeast;
+using testing::DoAll;
+using testing::Mock;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SetArgumentPointee;
+using testing::_;
+
+namespace chromeos_update_manager {
+
+class UmRealDevicePolicyProviderTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ provider_.reset(new RealDevicePolicyProvider(&mock_policy_provider_));
+ // By default, we have a device policy loaded. Tests can call
+ // SetUpNonExistentDevicePolicy() to override this.
+ SetUpExistentDevicePolicy();
+ }
+
+ virtual void TearDown() {
+ // Check for leaked callbacks on the main loop.
+ EXPECT_EQ(0, RunGMainLoopMaxIterations(100));
+ }
+
+ void SetUpNonExistentDevicePolicy() {
+ ON_CALL(mock_policy_provider_, Reload())
+ .WillByDefault(Return(false));
+ ON_CALL(mock_policy_provider_, device_policy_is_loaded())
+ .WillByDefault(Return(false));
+ EXPECT_CALL(mock_policy_provider_, GetDevicePolicy()).Times(0);
+ }
+
+ void SetUpExistentDevicePolicy() {
+ // Setup the default behavior of the mocked PolicyProvider.
+ ON_CALL(mock_policy_provider_, Reload())
+ .WillByDefault(Return(true));
+ ON_CALL(mock_policy_provider_, device_policy_is_loaded())
+ .WillByDefault(Return(true));
+ ON_CALL(mock_policy_provider_, GetDevicePolicy())
+ .WillByDefault(ReturnRef(mock_device_policy_));
+ }
+
+ testing::NiceMock<policy::MockDevicePolicy> mock_device_policy_;
+ testing::NiceMock<policy::MockPolicyProvider> mock_policy_provider_;
+ scoped_ptr<RealDevicePolicyProvider> provider_;
+};
+
+TEST_F(UmRealDevicePolicyProviderTest, RefreshScheduledTest) {
+ // Check that the RefreshPolicy gets scheduled by checking the EventId.
+ EXPECT_TRUE(provider_->Init());
+ EXPECT_NE(kEventIdNull, provider_->scheduled_refresh_);
+}
+
+TEST_F(UmRealDevicePolicyProviderTest, FirstReload) {
+ // Checks that the policy is reloaded and the DevicePolicy is consulted.
+ EXPECT_CALL(mock_policy_provider_, Reload());
+ EXPECT_TRUE(provider_->Init());
+}
+
+TEST_F(UmRealDevicePolicyProviderTest, NonExistentDevicePolicyReloaded) {
+ // Checks that the policy is reloaded by RefreshDevicePolicy().
+ SetUpNonExistentDevicePolicy();
+ EXPECT_CALL(mock_policy_provider_, Reload()).Times(2);
+ EXPECT_TRUE(provider_->Init());
+ // Force the policy refresh.
+ provider_->RefreshDevicePolicy();
+}
+
+TEST_F(UmRealDevicePolicyProviderTest, NonExistentDevicePolicyEmptyVariables) {
+ SetUpNonExistentDevicePolicy();
+ EXPECT_CALL(mock_policy_provider_, GetDevicePolicy()).Times(0);
+ EXPECT_TRUE(provider_->Init());
+
+ UmTestUtils::ExpectVariableHasValue(false,
+ provider_->var_device_policy_is_loaded());
+
+ UmTestUtils::ExpectVariableNotSet(provider_->var_release_channel());
+ UmTestUtils::ExpectVariableNotSet(provider_->var_release_channel_delegated());
+ UmTestUtils::ExpectVariableNotSet(provider_->var_update_disabled());
+ UmTestUtils::ExpectVariableNotSet(provider_->var_target_version_prefix());
+ UmTestUtils::ExpectVariableNotSet(provider_->var_scatter_factor());
+ UmTestUtils::ExpectVariableNotSet(
+ provider_->var_allowed_connection_types_for_update());
+ UmTestUtils::ExpectVariableNotSet(provider_->var_get_owner());
+ UmTestUtils::ExpectVariableNotSet(provider_->var_http_downloads_enabled());
+ UmTestUtils::ExpectVariableNotSet(provider_->var_au_p2p_enabled());
+}
+
+TEST_F(UmRealDevicePolicyProviderTest, ValuesUpdated) {
+ SetUpNonExistentDevicePolicy();
+ EXPECT_TRUE(provider_->Init());
+ Mock::VerifyAndClearExpectations(&mock_policy_provider_);
+
+ // Reload the policy with a good one and set some values as present. The
+ // remaining values are false.
+ SetUpExistentDevicePolicy();
+ EXPECT_CALL(mock_device_policy_, GetReleaseChannel(_))
+ .WillOnce(DoAll(SetArgumentPointee<0>(string("mychannel")),
+ Return(true)));
+ EXPECT_CALL(mock_device_policy_, GetAllowedConnectionTypesForUpdate(_))
+ .WillOnce(Return(false));
+
+ provider_->RefreshDevicePolicy();
+
+ UmTestUtils::ExpectVariableHasValue(true,
+ provider_->var_device_policy_is_loaded());
+
+ // Test that at least one variable is set, to ensure the refresh ocurred.
+ UmTestUtils::ExpectVariableHasValue(string("mychannel"),
+ provider_->var_release_channel());
+ UmTestUtils::ExpectVariableNotSet(
+ provider_->var_allowed_connection_types_for_update());
+}
+
+TEST_F(UmRealDevicePolicyProviderTest, ScatterFactorConverted) {
+ SetUpExistentDevicePolicy();
+ EXPECT_CALL(mock_device_policy_, GetScatterFactorInSeconds(_))
+ .WillOnce(DoAll(SetArgumentPointee<0>(1234), Return(true)));
+ EXPECT_TRUE(provider_->Init());
+
+ UmTestUtils::ExpectVariableHasValue(base::TimeDelta::FromSeconds(1234),
+ provider_->var_scatter_factor());
+}
+
+TEST_F(UmRealDevicePolicyProviderTest, NegativeScatterFactorIgnored) {
+ SetUpExistentDevicePolicy();
+ EXPECT_CALL(mock_device_policy_, GetScatterFactorInSeconds(_))
+ .WillOnce(DoAll(SetArgumentPointee<0>(-1), Return(true)));
+ EXPECT_TRUE(provider_->Init());
+
+ UmTestUtils::ExpectVariableNotSet(provider_->var_scatter_factor());
+}
+
+TEST_F(UmRealDevicePolicyProviderTest, AllowedTypesConverted) {
+ SetUpExistentDevicePolicy();
+ EXPECT_CALL(mock_device_policy_, GetAllowedConnectionTypesForUpdate(_))
+ .WillOnce(DoAll(SetArgumentPointee<0>(
+ set<string>{"bluetooth", "wifi", "not-a-type"}),
+ Return(true)));
+ EXPECT_TRUE(provider_->Init());
+
+ UmTestUtils::ExpectVariableHasValue(
+ set<ConnectionType>{ConnectionType::kWifi, ConnectionType::kBluetooth},
+ provider_->var_allowed_connection_types_for_update());
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/real_random_provider.cc b/update_manager/real_random_provider.cc
new file mode 100644
index 0000000..c151c4f
--- /dev/null
+++ b/update_manager/real_random_provider.cc
@@ -0,0 +1,74 @@
+// 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 <stdio.h>
+#include <unistd.h>
+
+#include <base/files/file_path.h>
+#include <base/files/scoped_file.h>
+#include <base/strings/stringprintf.h>
+
+#include "update_engine/update_manager/real_random_provider.h"
+#include "update_engine/update_manager/variable.h"
+
+using std::string;
+
+namespace {
+
+// The device providing randomness.
+const char* kRandomDevice = "/dev/urandom";
+
+} // namespace
+
+namespace chromeos_update_manager {
+
+// A random seed variable.
+class RandomSeedVariable : public Variable<uint64_t> {
+ public:
+ // RandomSeedVariable is initialized as kVariableModeConst to let the
+ // EvaluationContext cache the value between different evaluations of the same
+ // policy request.
+ RandomSeedVariable(const string& name, FILE* fp)
+ : Variable<uint64_t>(name, kVariableModeConst), fp_(fp) {}
+ virtual ~RandomSeedVariable() {}
+
+ protected:
+ virtual const uint64_t* GetValue(base::TimeDelta /* timeout */,
+ string* errmsg) {
+ uint64_t result;
+ // Aliasing via char pointer abides by the C/C++ strict-aliasing rules.
+ char* const buf = reinterpret_cast<char*>(&result);
+ unsigned int buf_rd = 0;
+
+ while (buf_rd < sizeof(result)) {
+ int rd = fread(buf + buf_rd, 1, sizeof(result) - buf_rd, fp_.get());
+ if (rd == 0 || ferror(fp_.get())) {
+ // Either EOF on fp or read failed.
+ if (errmsg) {
+ *errmsg = base::StringPrintf(
+ "Error reading from the random device: %s", kRandomDevice);
+ }
+ return NULL;
+ }
+ buf_rd += rd;
+ }
+
+ return new uint64_t(result);
+ }
+
+ private:
+ base::ScopedFILE fp_;
+
+ DISALLOW_COPY_AND_ASSIGN(RandomSeedVariable);
+};
+
+bool RealRandomProvider::Init(void) {
+ FILE* fp = fopen(kRandomDevice, "r");
+ if (!fp)
+ return false;
+ var_seed_.reset(new RandomSeedVariable("seed", fp));
+ return true;
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/real_random_provider.h b/update_manager/real_random_provider.h
new file mode 100644
index 0000000..d1de79e
--- /dev/null
+++ b/update_manager/real_random_provider.h
@@ -0,0 +1,33 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_RANDOM_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_RANDOM_PROVIDER_H_
+
+#include <base/memory/scoped_ptr.h>
+
+#include "update_engine/update_manager/random_provider.h"
+
+namespace chromeos_update_manager {
+
+// RandomProvider implementation class.
+class RealRandomProvider : public RandomProvider {
+ public:
+ RealRandomProvider() {}
+
+ virtual Variable<uint64_t>* var_seed() override { return var_seed_.get(); }
+
+ // Initializes the provider and returns whether it succeeded.
+ bool Init();
+
+ private:
+ // The seed() scoped variable.
+ scoped_ptr<Variable<uint64_t>> var_seed_;
+
+ DISALLOW_COPY_AND_ASSIGN(RealRandomProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_RANDOM_PROVIDER_H_
diff --git a/update_manager/real_random_provider_unittest.cc b/update_manager/real_random_provider_unittest.cc
new file mode 100644
index 0000000..55e17fa
--- /dev/null
+++ b/update_manager/real_random_provider_unittest.cc
@@ -0,0 +1,53 @@
+// 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 <base/memory/scoped_ptr.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/update_manager/real_random_provider.h"
+#include "update_engine/update_manager/umtest_utils.h"
+
+using base::TimeDelta;
+
+namespace chromeos_update_manager {
+
+class UmRealRandomProviderTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ // The provider initializes correctly.
+ provider_.reset(new RealRandomProvider());
+ UMTEST_ASSERT_NOT_NULL(provider_.get());
+ ASSERT_TRUE(provider_->Init());
+
+ provider_->var_seed();
+ }
+
+ scoped_ptr<RealRandomProvider> provider_;
+};
+
+TEST_F(UmRealRandomProviderTest, InitFinalize) {
+ // The provider initializes all variables with valid objects.
+ UMTEST_EXPECT_NOT_NULL(provider_->var_seed());
+}
+
+TEST_F(UmRealRandomProviderTest, GetRandomValues) {
+ // Should not return the same random seed repeatedly.
+ scoped_ptr<const uint64_t> value(
+ provider_->var_seed()->GetValue(UmTestUtils::DefaultTimeout(), nullptr));
+ UMTEST_ASSERT_NOT_NULL(value.get());
+
+ // Test that at least the returned values are different. This test fails,
+ // by design, once every 2^320 runs.
+ bool is_same_value = true;
+ for (int i = 0; i < 5; i++) {
+ scoped_ptr<const uint64_t> other_value(
+ provider_->var_seed()->GetValue(UmTestUtils::DefaultTimeout(),
+ nullptr));
+ UMTEST_ASSERT_NOT_NULL(other_value.get());
+ is_same_value = is_same_value && *other_value == *value;
+ }
+ EXPECT_FALSE(is_same_value);
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/real_shill_provider.cc b/update_manager/real_shill_provider.cc
new file mode 100644
index 0000000..b7a36d5
--- /dev/null
+++ b/update_manager/real_shill_provider.cc
@@ -0,0 +1,191 @@
+// 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/update_manager/real_shill_provider.h"
+
+#include <string>
+
+#include <base/logging.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/dbus/service_constants.h>
+
+#include "update_engine/utils.h"
+
+using std::string;
+
+namespace {
+
+// Looks up a |key| in a GLib |hash_table| and returns the unboxed string from
+// the corresponding GValue, if found.
+const char* GetStrProperty(GHashTable* hash_table, const char* key) {
+ auto gval = reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table, key));
+ return (gval ? g_value_get_string(gval) : NULL);
+}
+
+}; // namespace
+
+
+namespace chromeos_update_manager {
+
+RealShillProvider::~RealShillProvider() {
+ // Detach signal handler, free manager proxy.
+ dbus_->ProxyDisconnectSignal(manager_proxy_, shill::kMonitorPropertyChanged,
+ G_CALLBACK(HandlePropertyChangedStatic),
+ this);
+ dbus_->ProxyUnref(manager_proxy_);
+}
+
+ConnectionType RealShillProvider::ParseConnectionType(const char* type_str) {
+ if (!strcmp(type_str, shill::kTypeEthernet))
+ return ConnectionType::kEthernet;
+ if (!strcmp(type_str, shill::kTypeWifi))
+ return ConnectionType::kWifi;
+ if (!strcmp(type_str, shill::kTypeWimax))
+ return ConnectionType::kWimax;
+ if (!strcmp(type_str, shill::kTypeBluetooth))
+ return ConnectionType::kBluetooth;
+ if (!strcmp(type_str, shill::kTypeCellular))
+ return ConnectionType::kCellular;
+
+ return ConnectionType::kUnknown;
+}
+
+ConnectionTethering RealShillProvider::ParseConnectionTethering(
+ const char* tethering_str) {
+ if (!strcmp(tethering_str, shill::kTetheringNotDetectedState))
+ return ConnectionTethering::kNotDetected;
+ if (!strcmp(tethering_str, shill::kTetheringSuspectedState))
+ return ConnectionTethering::kSuspected;
+ if (!strcmp(tethering_str, shill::kTetheringConfirmedState))
+ return ConnectionTethering::kConfirmed;
+
+ return ConnectionTethering::kUnknown;
+}
+
+bool RealShillProvider::Init() {
+ // Obtain a DBus connection.
+ GError* error = NULL;
+ connection_ = dbus_->BusGet(DBUS_BUS_SYSTEM, &error);
+ if (!connection_) {
+ LOG(ERROR) << "Failed to initialize DBus connection: "
+ << chromeos_update_engine::utils::GetAndFreeGError(&error);
+ return false;
+ }
+
+ // Allocate a shill manager proxy.
+ manager_proxy_ = GetProxy(shill::kFlimflamServicePath,
+ shill::kFlimflamManagerInterface);
+
+ // Subscribe to the manager's PropertyChanged signal.
+ dbus_->ProxyAddSignal_2(manager_proxy_, shill::kMonitorPropertyChanged,
+ G_TYPE_STRING, G_TYPE_VALUE);
+ dbus_->ProxyConnectSignal(manager_proxy_, shill::kMonitorPropertyChanged,
+ G_CALLBACK(HandlePropertyChangedStatic),
+ this, NULL);
+
+ // Attempt to read initial connection status. Even if this fails because shill
+ // is not responding (e.g. it is down) we'll be notified via "PropertyChanged"
+ // signal as soon as it comes up, so this is not a critical step.
+ GHashTable* hash_table = NULL;
+ if (GetProperties(manager_proxy_, &hash_table)) {
+ GValue* value = reinterpret_cast<GValue*>(
+ g_hash_table_lookup(hash_table, shill::kDefaultServiceProperty));
+ ProcessDefaultService(value);
+ g_hash_table_unref(hash_table);
+ }
+
+ return true;
+}
+
+DBusGProxy* RealShillProvider::GetProxy(const char* path,
+ const char* interface) {
+ return dbus_->ProxyNewForName(connection_, shill::kFlimflamServiceName,
+ path, interface);
+}
+
+bool RealShillProvider::GetProperties(DBusGProxy* proxy,
+ GHashTable** result_p) {
+ GError* error = NULL;
+ if (!dbus_->ProxyCall_0_1(proxy, shill::kGetPropertiesFunction, &error,
+ result_p)) {
+ LOG(ERROR) << "Calling shill via DBus proxy failed: "
+ << chromeos_update_engine::utils::GetAndFreeGError(&error);
+ return false;
+ }
+ return true;
+}
+
+bool RealShillProvider::ProcessDefaultService(GValue* value) {
+ // Decode the string from the boxed value.
+ const char* default_service_path_str = NULL;
+ if (!(value && (default_service_path_str = g_value_get_string(value))))
+ return false;
+
+ // Anything changed?
+ if (default_service_path_ == default_service_path_str)
+ return true;
+
+ // Update the connection status.
+ default_service_path_ = default_service_path_str;
+ bool is_connected = (default_service_path_ != "/");
+ var_is_connected_.SetValue(is_connected);
+ var_conn_last_changed_.SetValue(clock_->GetWallclockTime());
+
+ // Update the connection attributes.
+ if (is_connected) {
+ DBusGProxy* service_proxy = GetProxy(default_service_path_.c_str(),
+ shill::kFlimflamServiceInterface);
+ GHashTable* hash_table = NULL;
+ if (GetProperties(service_proxy, &hash_table)) {
+ // Get the connection type.
+ const char* type_str = GetStrProperty(hash_table, shill::kTypeProperty);
+ if (type_str && !strcmp(type_str, shill::kTypeVPN)) {
+ type_str = GetStrProperty(hash_table,
+ shill::kPhysicalTechnologyProperty);
+ }
+ if (type_str) {
+ var_conn_type_.SetValue(ParseConnectionType(type_str));
+ } else {
+ var_conn_type_.UnsetValue();
+ LOG(ERROR) << "Could not find connection type ("
+ << default_service_path_ << ")";
+ }
+
+ // Get the connection tethering mode.
+ const char* tethering_str = GetStrProperty(hash_table,
+ shill::kTetheringProperty);
+ if (tethering_str) {
+ var_conn_tethering_.SetValue(ParseConnectionTethering(tethering_str));
+ } else {
+ var_conn_tethering_.UnsetValue();
+ LOG(ERROR) << "Could not find connection tethering mode ("
+ << default_service_path_ << ")";
+ }
+
+ g_hash_table_unref(hash_table);
+ }
+ dbus_->ProxyUnref(service_proxy);
+ } else {
+ var_conn_type_.UnsetValue();
+ var_conn_tethering_.UnsetValue();
+ }
+
+ return true;
+}
+
+void RealShillProvider::HandlePropertyChanged(DBusGProxy* proxy,
+ const char* name, GValue* value) {
+ if (!strcmp(name, shill::kDefaultServiceProperty))
+ ProcessDefaultService(value);
+}
+
+void RealShillProvider::HandlePropertyChangedStatic(DBusGProxy* proxy,
+ const char* name,
+ GValue* value,
+ void* data) {
+ auto obj = reinterpret_cast<RealShillProvider*>(data);
+ obj->HandlePropertyChanged(proxy, name, value);
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/real_shill_provider.h b/update_manager/real_shill_provider.h
new file mode 100644
index 0000000..df4f622
--- /dev/null
+++ b/update_manager/real_shill_provider.h
@@ -0,0 +1,97 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_SHILL_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_SHILL_PROVIDER_H_
+
+// TODO(garnold) Much of the functionality in this module was adapted from the
+// update engine's connection_manager. We need to make sure to deprecate use of
+// connection manager when the time comes.
+
+#include <string>
+
+#include <base/time/time.h>
+
+#include "update_engine/clock_interface.h"
+#include "update_engine/dbus_wrapper_interface.h"
+#include "update_engine/update_manager/generic_variables.h"
+#include "update_engine/update_manager/shill_provider.h"
+
+using chromeos_update_engine::ClockInterface;
+using chromeos_update_engine::DBusWrapperInterface;
+
+namespace chromeos_update_manager {
+
+// ShillProvider concrete implementation.
+class RealShillProvider : public ShillProvider {
+ public:
+ RealShillProvider(DBusWrapperInterface* dbus, ClockInterface* clock)
+ : dbus_(dbus), clock_(clock) {}
+
+ virtual ~RealShillProvider();
+
+ // Initializes the provider and returns whether it succeeded.
+ bool Init();
+
+ virtual Variable<bool>* var_is_connected() override {
+ return &var_is_connected_;
+ }
+
+ virtual Variable<ConnectionType>* var_conn_type() override {
+ return &var_conn_type_;
+ }
+
+ virtual Variable<ConnectionTethering>* var_conn_tethering() override {
+ return &var_conn_tethering_;
+ }
+
+ virtual Variable<base::Time>* var_conn_last_changed() override {
+ return &var_conn_last_changed_;
+ }
+
+ // Helper methods for converting shill strings into symbolic values.
+ static ConnectionType ParseConnectionType(const char* type_str);
+ static ConnectionTethering ParseConnectionTethering(
+ const char* tethering_str);
+
+ private:
+ // Return a DBus proxy for a given |path| and |interface| within shill.
+ DBusGProxy* GetProxy(const char* path, const char* interface);
+
+ // Issues a GetProperties call through a given |proxy|, storing the result to
+ // |*result_p|. Returns true on success.
+ bool GetProperties(DBusGProxy* proxy, GHashTable** result_p);
+
+ // Process a default connection value, update last change time as needed.
+ bool ProcessDefaultService(GValue* value);
+
+ // A handler for manager PropertyChanged signal, and a static version.
+ void HandlePropertyChanged(DBusGProxy* proxy, const char *name,
+ GValue* value);
+ static void HandlePropertyChangedStatic(DBusGProxy* proxy, const char* name,
+ GValue* value, void* data);
+
+ // The current default service path, if connected.
+ std::string default_service_path_;
+
+ // The DBus interface (mockable), connection, and a shill manager proxy.
+ DBusWrapperInterface* const dbus_;
+ DBusGConnection* connection_ = NULL;
+ DBusGProxy* manager_proxy_ = NULL;
+
+ // A clock abstraction (mockable).
+ ClockInterface* const clock_;
+
+ // The provider's variables.
+ AsyncCopyVariable<bool> var_is_connected_{"is_connected"};
+ AsyncCopyVariable<ConnectionType> var_conn_type_{"conn_type"};
+ AsyncCopyVariable<ConnectionTethering> var_conn_tethering_{"conn_tethering"};
+ AsyncCopyVariable<base::Time> var_conn_last_changed_{"conn_last_changed"};
+
+ DISALLOW_COPY_AND_ASSIGN(RealShillProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_SHILL_PROVIDER_H_
diff --git a/update_manager/real_shill_provider_unittest.cc b/update_manager/real_shill_provider_unittest.cc
new file mode 100644
index 0000000..faa6e05
--- /dev/null
+++ b/update_manager/real_shill_provider_unittest.cc
@@ -0,0 +1,540 @@
+// 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 <utility>
+
+#include <base/memory/scoped_ptr.h>
+#include <base/time/time.h>
+#include <chromeos/dbus/service_constants.h>
+#include <glib.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/fake_clock.h"
+#include "update_engine/mock_dbus_wrapper.h"
+#include "update_engine/test_utils.h"
+#include "update_engine/update_manager/real_shill_provider.h"
+#include "update_engine/update_manager/umtest_utils.h"
+
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_engine::FakeClock;
+using chromeos_update_engine::GValueFree;
+using chromeos_update_engine::GValueNewString;
+using chromeos_update_engine::MockDBusWrapper;
+using std::pair;
+using testing::Eq;
+using testing::Mock;
+using testing::NiceMock;
+using testing::Return;
+using testing::SaveArg;
+using testing::SetArgPointee;
+using testing::StrEq;
+using testing::StrictMock;
+using testing::_;
+
+namespace {
+
+// Fake dbus-glib objects. These should be different values, to ease diagnosis
+// of errors.
+DBusGConnection* const kFakeConnection = reinterpret_cast<DBusGConnection*>(1);
+DBusGProxy* const kFakeManagerProxy = reinterpret_cast<DBusGProxy*>(2);
+DBusGProxy* const kFakeEthernetServiceProxy = reinterpret_cast<DBusGProxy*>(3);
+DBusGProxy* const kFakeWifiServiceProxy = reinterpret_cast<DBusGProxy*>(4);
+DBusGProxy* const kFakeWimaxServiceProxy = reinterpret_cast<DBusGProxy*>(5);
+DBusGProxy* const kFakeBluetoothServiceProxy = reinterpret_cast<DBusGProxy*>(6);
+DBusGProxy* const kFakeCellularServiceProxy = reinterpret_cast<DBusGProxy*>(7);
+DBusGProxy* const kFakeVpnServiceProxy = reinterpret_cast<DBusGProxy*>(8);
+DBusGProxy* const kFakeUnknownServiceProxy = reinterpret_cast<DBusGProxy*>(9);
+
+// Fake service paths.
+const char* const kFakeEthernetServicePath = "/fake-ethernet-service";
+const char* const kFakeWifiServicePath = "/fake-wifi-service";
+const char* const kFakeWimaxServicePath = "/fake-wimax-service";
+const char* const kFakeBluetoothServicePath = "/fake-bluetooth-service";
+const char* const kFakeCellularServicePath = "/fake-cellular-service";
+const char* const kFakeVpnServicePath = "/fake-vpn-service";
+const char* const kFakeUnknownServicePath = "/fake-unknown-service";
+
+} // namespace
+
+namespace chromeos_update_manager {
+
+class UmRealShillProviderTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ // By default, initialize the provider so that it gets an initial connection
+ // status from shill. This simulates the common case where shill is
+ // available and respoding during RealShillProvider initialization.
+ Init(true);
+ }
+
+ virtual void TearDown() {
+ Shutdown();
+ }
+
+ // Initialize the RealShillProvider under test. If |do_init_conn_status| is
+ // true, configure mocks to respond to the initial connection status check
+ // with shill. Otherwise, the initial check will fail.
+ void Init(bool do_init_conn_status) {
+ // Properly shutdown a previously initialized provider.
+ if (provider_.get())
+ Shutdown();
+
+ provider_.reset(new RealShillProvider(&mock_dbus_, &fake_clock_));
+ UMTEST_ASSERT_NOT_NULL(provider_.get());
+ fake_clock_.SetWallclockTime(InitTime());
+
+ // A DBus connection should only be obtained once.
+ EXPECT_CALL(mock_dbus_, BusGet(_, _)).WillOnce(
+ Return(kFakeConnection));
+
+ // A manager proxy should only be obtained once.
+ EXPECT_CALL(mock_dbus_, ProxyNewForName(
+ kFakeConnection, StrEq(shill::kFlimflamServiceName),
+ StrEq(shill::kFlimflamServicePath),
+ StrEq(shill::kFlimflamManagerInterface)))
+ .WillOnce(Return(kFakeManagerProxy));
+
+ // The PropertyChanged signal should be subscribed to.
+ EXPECT_CALL(mock_dbus_, ProxyAddSignal_2(
+ kFakeManagerProxy, StrEq(shill::kMonitorPropertyChanged),
+ G_TYPE_STRING, G_TYPE_VALUE))
+ .WillOnce(Return());
+ EXPECT_CALL(mock_dbus_, ProxyConnectSignal(
+ kFakeManagerProxy, StrEq(shill::kMonitorPropertyChanged),
+ _, _, _))
+ .WillOnce(
+ DoAll(SaveArg<2>(reinterpret_cast<void (**)()>(&signal_handler_)),
+ SaveArg<3>(&signal_data_),
+ Return()));
+
+ // Mock a response to an initial connection check (optional).
+ GHashTable* manager_properties = nullptr;
+ if (do_init_conn_status) {
+ pair<const char*, const char*> manager_pairs[] = {
+ {shill::kDefaultServiceProperty, "/"},
+ };
+ manager_properties = SetupGetPropertiesOkay(
+ kFakeManagerProxy, arraysize(manager_pairs), manager_pairs);
+ } else {
+ SetupGetPropertiesFail(kFakeManagerProxy);
+ }
+
+ // Check that provider initializes corrrectly.
+ ASSERT_TRUE(provider_->Init());
+
+ // All mocked calls should have been exercised by now.
+ Mock::VerifyAndClear(&mock_dbus_);
+
+ // Release properties hash table (if provided).
+ if (manager_properties)
+ g_hash_table_unref(manager_properties);
+ }
+
+ // Deletes the RealShillProvider under test.
+ void Shutdown() {
+ // Make sure that DBus resources get freed.
+ EXPECT_CALL(mock_dbus_, ProxyDisconnectSignal(
+ kFakeManagerProxy, StrEq(shill::kMonitorPropertyChanged),
+ Eq(reinterpret_cast<void (*)()>(signal_handler_)),
+ Eq(signal_data_)))
+ .WillOnce(Return());
+ EXPECT_CALL(mock_dbus_, ProxyUnref(kFakeManagerProxy)).WillOnce(Return());
+ provider_.reset();
+
+ // All mocked calls should have been exercised by now.
+ Mock::VerifyAndClear(&mock_dbus_);
+ }
+
+ // These methods generate fixed timestamps for use in faking the current time.
+ Time InitTime() {
+ Time::Exploded now_exp;
+ now_exp.year = 2014;
+ now_exp.month = 3;
+ now_exp.day_of_week = 2;
+ now_exp.day_of_month = 18;
+ now_exp.hour = 8;
+ now_exp.minute = 5;
+ now_exp.second = 33;
+ now_exp.millisecond = 675;
+ return Time::FromLocalExploded(now_exp);
+ }
+
+ Time ConnChangedTime() {
+ return InitTime() + TimeDelta::FromSeconds(10);
+ }
+
+ // Sets up a successful mock "GetProperties" call on |proxy|, writing a hash
+ // table containing |num_entries| entries formed by key/value pairs from
+ // |key_val_pairs| and returning true. Keys and values are plain C strings
+ // (const char*). The proxy call is expected to be made exactly once. Returns
+ // a pointer to a newly allocated hash table, which should be unreffed with
+ // g_hash_table_unref() when done.
+ GHashTable* SetupGetPropertiesOkay(
+ DBusGProxy* proxy, size_t num_entries,
+ pair<const char*, const char*>* key_val_pairs) {
+ // Allocate and populate the hash table.
+ GHashTable* properties = g_hash_table_new_full(g_str_hash, g_str_equal,
+ free, GValueFree);
+ for (size_t i = 0; i < num_entries; i++) {
+ g_hash_table_insert(properties, strdup(key_val_pairs[i].first),
+ GValueNewString(key_val_pairs[i].second));
+ }
+
+ // Set mock expectations.
+ EXPECT_CALL(mock_dbus_,
+ ProxyCall_0_1(proxy, StrEq(shill::kGetPropertiesFunction),
+ _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(g_hash_table_ref(properties)),
+ Return(true)));
+
+ return properties;
+ }
+
+ // Sets up a failing mock "GetProperties" call on |proxy|, returning false.
+ // The proxy call is expected to be made exactly once.
+ void SetupGetPropertiesFail(DBusGProxy* proxy) {
+ EXPECT_CALL(mock_dbus_,
+ ProxyCall_0_1(proxy, StrEq(shill::kGetPropertiesFunction),
+ _, _))
+ .WillOnce(Return(false));
+ }
+
+ // Sends a signal informing the provider about a default connection
+ // |service_path|. Returns the fake connection change time.
+ Time SendDefaultServiceSignal(const char* service_path) {
+ auto default_service_gval = GValueNewString(service_path);
+ const Time conn_change_time = ConnChangedTime();
+ fake_clock_.SetWallclockTime(conn_change_time);
+ signal_handler_(kFakeManagerProxy, shill::kDefaultServiceProperty,
+ default_service_gval, signal_data_);
+ fake_clock_.SetWallclockTime(conn_change_time + TimeDelta::FromSeconds(5));
+ GValueFree(default_service_gval);
+ return conn_change_time;
+ }
+
+ // Sets up expectations for detection of a connection |service_path| with type
+ // |shill_type_str| and tethering mode |shill_tethering_str|. Ensures that the
+ // new connection status and change time are properly detected by the
+ // provider. Writes the fake connection change time to |conn_change_time_p|,
+ // if provided.
+ void SetupConnectionAndAttrs(const char* service_path,
+ DBusGProxy* service_proxy,
+ const char* shill_type_str,
+ const char* shill_tethering_str,
+ Time* conn_change_time_p) {
+ // Mock logic for querying the default service attributes.
+ EXPECT_CALL(mock_dbus_,
+ ProxyNewForName(
+ kFakeConnection, StrEq(shill::kFlimflamServiceName),
+ StrEq(service_path),
+ StrEq(shill::kFlimflamServiceInterface)))
+ .WillOnce(Return(service_proxy));
+ EXPECT_CALL(mock_dbus_, ProxyUnref(service_proxy)).WillOnce(Return());
+ pair<const char*, const char*> service_pairs[] = {
+ {shill::kTypeProperty, shill_type_str},
+ {shill::kTetheringProperty, shill_tethering_str},
+ };
+ auto service_properties = SetupGetPropertiesOkay(
+ service_proxy, arraysize(service_pairs), service_pairs);
+
+ // Send a signal about a new default service.
+ auto conn_change_time = SendDefaultServiceSignal(service_path);
+
+ // Release the service properties hash tables.
+ g_hash_table_unref(service_properties);
+
+ // Query the connection status, ensure last change time reported correctly.
+ UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected());
+ UmTestUtils::ExpectVariableHasValue(conn_change_time,
+ provider_->var_conn_last_changed());
+
+ // Write the connection change time to the output argument.
+ if (conn_change_time_p)
+ *conn_change_time_p = conn_change_time;
+ }
+
+ // Sets up a connection and tests that its type is being properly detected by
+ // the provider.
+ void SetupConnectionAndTestType(const char* service_path,
+ DBusGProxy* service_proxy,
+ const char* shill_type_str,
+ ConnectionType expected_conn_type) {
+ // Set up and test the connection, record the change time.
+ Time conn_change_time;
+ SetupConnectionAndAttrs(service_path, service_proxy, shill_type_str,
+ shill::kTetheringNotDetectedState,
+ &conn_change_time);
+
+ // Query the connection type, ensure last change time did not change.
+ UmTestUtils::ExpectVariableHasValue(expected_conn_type,
+ provider_->var_conn_type());
+ UmTestUtils::ExpectVariableHasValue(conn_change_time,
+ provider_->var_conn_last_changed());
+ }
+
+ // Sets up a connection and tests that its tethering mode is being properly
+ // detected by the provider.
+ void SetupConnectionAndTestTethering(
+ const char* service_path, DBusGProxy* service_proxy,
+ const char* shill_tethering_str,
+ ConnectionTethering expected_conn_tethering) {
+ // Set up and test the connection, record the change time.
+ Time conn_change_time;
+ SetupConnectionAndAttrs(service_path, service_proxy, shill::kTypeEthernet,
+ shill_tethering_str, &conn_change_time);
+
+ // Query the connection tethering, ensure last change time did not change.
+ UmTestUtils::ExpectVariableHasValue(expected_conn_tethering,
+ provider_->var_conn_tethering());
+ UmTestUtils::ExpectVariableHasValue(conn_change_time,
+ provider_->var_conn_last_changed());
+ }
+
+ StrictMock<MockDBusWrapper> mock_dbus_;
+ FakeClock fake_clock_;
+ scoped_ptr<RealShillProvider> provider_;
+ void (*signal_handler_)(DBusGProxy*, const char*, GValue*, void*);
+ void* signal_data_;
+};
+
+// Query the connection status, type and time last changed, as they were set
+// during initialization (no signals).
+TEST_F(UmRealShillProviderTest, ReadBaseValues) {
+ // Query the provider variables.
+ UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_connected());
+ UmTestUtils::ExpectVariableNotSet(provider_->var_conn_type());
+ UmTestUtils::ExpectVariableHasValue(InitTime(),
+ provider_->var_conn_last_changed());
+}
+
+// Test that Ethernet connection is identified correctly.
+TEST_F(UmRealShillProviderTest, ReadConnTypeEthernet) {
+ SetupConnectionAndTestType(kFakeEthernetServicePath,
+ kFakeEthernetServiceProxy,
+ shill::kTypeEthernet,
+ ConnectionType::kEthernet);
+}
+
+// Test that Wifi connection is identified correctly.
+TEST_F(UmRealShillProviderTest, ReadConnTypeWifi) {
+ SetupConnectionAndTestType(kFakeWifiServicePath,
+ kFakeWifiServiceProxy,
+ shill::kTypeWifi,
+ ConnectionType::kWifi);
+}
+
+// Test that Wimax connection is identified correctly.
+TEST_F(UmRealShillProviderTest, ReadConnTypeWimax) {
+ SetupConnectionAndTestType(kFakeWimaxServicePath,
+ kFakeWimaxServiceProxy,
+ shill::kTypeWimax,
+ ConnectionType::kWimax);
+}
+
+// Test that Bluetooth connection is identified correctly.
+TEST_F(UmRealShillProviderTest, ReadConnTypeBluetooth) {
+ SetupConnectionAndTestType(kFakeBluetoothServicePath,
+ kFakeBluetoothServiceProxy,
+ shill::kTypeBluetooth,
+ ConnectionType::kBluetooth);
+}
+
+// Test that Cellular connection is identified correctly.
+TEST_F(UmRealShillProviderTest, ReadConnTypeCellular) {
+ SetupConnectionAndTestType(kFakeCellularServicePath,
+ kFakeCellularServiceProxy,
+ shill::kTypeCellular,
+ ConnectionType::kCellular);
+}
+
+// Test that an unknown connection is identified as such.
+TEST_F(UmRealShillProviderTest, ReadConnTypeUnknown) {
+ SetupConnectionAndTestType(kFakeUnknownServicePath,
+ kFakeUnknownServiceProxy,
+ "FooConnectionType",
+ ConnectionType::kUnknown);
+}
+
+// Tests that VPN connection is identified correctly.
+TEST_F(UmRealShillProviderTest, ReadConnTypeVpn) {
+ // Mock logic for returning a default service path and its type.
+ EXPECT_CALL(mock_dbus_, ProxyNewForName(
+ kFakeConnection, StrEq(shill::kFlimflamServiceName),
+ StrEq(kFakeVpnServicePath), StrEq(shill::kFlimflamServiceInterface)))
+ .WillOnce(Return(kFakeVpnServiceProxy));
+ EXPECT_CALL(mock_dbus_, ProxyUnref(kFakeVpnServiceProxy)).WillOnce(Return());
+ pair<const char*, const char*> service_pairs[] = {
+ {shill::kTypeProperty, shill::kTypeVPN},
+ {shill::kPhysicalTechnologyProperty, shill::kTypeWifi},
+ };
+ auto service_properties = SetupGetPropertiesOkay(kFakeVpnServiceProxy,
+ arraysize(service_pairs),
+ service_pairs);
+
+ // Send a signal about a new default service.
+ Time conn_change_time = SendDefaultServiceSignal(kFakeVpnServicePath);
+
+ // Query the connection type, ensure last change time reported correctly.
+ UmTestUtils::ExpectVariableHasValue(ConnectionType::kWifi,
+ provider_->var_conn_type());
+ UmTestUtils::ExpectVariableHasValue(conn_change_time,
+ provider_->var_conn_last_changed());
+
+ // Release properties hash tables.
+ g_hash_table_unref(service_properties);
+}
+
+// Ensure that the connection type is properly cached in the provider through
+// subsequent variable readings.
+TEST_F(UmRealShillProviderTest, ConnTypeCacheUsed) {
+ SetupConnectionAndTestType(kFakeEthernetServicePath,
+ kFakeEthernetServiceProxy,
+ shill::kTypeEthernet,
+ ConnectionType::kEthernet);
+
+ UmTestUtils::ExpectVariableHasValue(ConnectionType::kEthernet,
+ provider_->var_conn_type());
+}
+
+// Ensure that the cached connection type remains valid even when a default
+// connection signal occurs but the connection is not changed.
+TEST_F(UmRealShillProviderTest, ConnTypeCacheRemainsValid) {
+ SetupConnectionAndTestType(kFakeEthernetServicePath,
+ kFakeEthernetServiceProxy,
+ shill::kTypeEthernet,
+ ConnectionType::kEthernet);
+
+ SendDefaultServiceSignal(kFakeEthernetServicePath);
+
+ UmTestUtils::ExpectVariableHasValue(ConnectionType::kEthernet,
+ provider_->var_conn_type());
+}
+
+// Ensure that the cached connection type is invalidated and re-read when the
+// default connection changes.
+TEST_F(UmRealShillProviderTest, ConnTypeCacheInvalidated) {
+ SetupConnectionAndTestType(kFakeEthernetServicePath,
+ kFakeEthernetServiceProxy,
+ shill::kTypeEthernet,
+ ConnectionType::kEthernet);
+
+ SetupConnectionAndTestType(kFakeWifiServicePath,
+ kFakeWifiServiceProxy,
+ shill::kTypeWifi,
+ ConnectionType::kWifi);
+}
+
+// Test that a non-tethering mode is identified correctly.
+TEST_F(UmRealShillProviderTest, ReadConnTetheringNotDetected) {
+ SetupConnectionAndTestTethering(kFakeWifiServicePath,
+ kFakeWifiServiceProxy,
+ shill::kTetheringNotDetectedState,
+ ConnectionTethering::kNotDetected);
+}
+
+// Test that a suspected tethering mode is identified correctly.
+TEST_F(UmRealShillProviderTest, ReadConnTetheringSuspected) {
+ SetupConnectionAndTestTethering(kFakeWifiServicePath,
+ kFakeWifiServiceProxy,
+ shill::kTetheringSuspectedState,
+ ConnectionTethering::kSuspected);
+}
+
+// Test that a confirmed tethering mode is identified correctly.
+TEST_F(UmRealShillProviderTest, ReadConnTetheringConfirmed) {
+ SetupConnectionAndTestTethering(kFakeWifiServicePath,
+ kFakeWifiServiceProxy,
+ shill::kTetheringConfirmedState,
+ ConnectionTethering::kConfirmed);
+}
+
+// Test that an unknown tethering mode is identified as such.
+TEST_F(UmRealShillProviderTest, ReadConnTetheringUnknown) {
+ SetupConnectionAndTestTethering(kFakeWifiServicePath,
+ kFakeWifiServiceProxy,
+ "FooConnTethering",
+ ConnectionTethering::kUnknown);
+}
+
+// Ensure that the connection tethering mode is properly cached in the provider.
+TEST_F(UmRealShillProviderTest, ConnTetheringCacheUsed) {
+ SetupConnectionAndTestTethering(kFakeEthernetServicePath,
+ kFakeEthernetServiceProxy,
+ shill::kTetheringNotDetectedState,
+ ConnectionTethering::kNotDetected);
+
+ UmTestUtils::ExpectVariableHasValue(ConnectionTethering::kNotDetected,
+ provider_->var_conn_tethering());
+}
+
+// Ensure that the cached connection tethering mode remains valid even when a
+// default connection signal occurs but the connection is not changed.
+TEST_F(UmRealShillProviderTest, ConnTetheringCacheRemainsValid) {
+ SetupConnectionAndTestTethering(kFakeEthernetServicePath,
+ kFakeEthernetServiceProxy,
+ shill::kTetheringNotDetectedState,
+ ConnectionTethering::kNotDetected);
+
+ SendDefaultServiceSignal(kFakeEthernetServicePath);
+
+ UmTestUtils::ExpectVariableHasValue(ConnectionTethering::kNotDetected,
+ provider_->var_conn_tethering());
+}
+
+// Ensure that the cached connection tethering mode is invalidated and re-read
+// when the default connection changes.
+TEST_F(UmRealShillProviderTest, ConnTetheringCacheInvalidated) {
+ SetupConnectionAndTestTethering(kFakeEthernetServicePath,
+ kFakeEthernetServiceProxy,
+ shill::kTetheringNotDetectedState,
+ ConnectionTethering::kNotDetected);
+
+ SetupConnectionAndTestTethering(kFakeWifiServicePath,
+ kFakeWifiServiceProxy,
+ shill::kTetheringConfirmedState,
+ ConnectionTethering::kConfirmed);
+}
+
+// Fake two DBus signals prompting a default connection change, but otherwise
+// give the same service path. Check connection status and the time it was last
+// changed, making sure that it is the time when the first signal was sent (and
+// not the second).
+TEST_F(UmRealShillProviderTest, ReadLastChangedTimeTwoSignals) {
+ // Send a default service signal twice, advancing the clock in between.
+ Time conn_change_time;
+ SetupConnectionAndAttrs(kFakeEthernetServicePath, kFakeEthernetServiceProxy,
+ shill::kTypeEthernet,
+ shill::kTetheringNotDetectedState, &conn_change_time);
+ SendDefaultServiceSignal(kFakeEthernetServicePath);
+
+ // Query the connection status, ensure last change time reported correctly.
+ UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected());
+ UmTestUtils::ExpectVariableHasValue(conn_change_time,
+ provider_->var_conn_last_changed());
+}
+
+// Make sure that the provider initializes correctly even if shill is not
+// responding, that variables can be obtained, and that they all return a null
+// value (indicating that the underlying values were not set).
+TEST_F(UmRealShillProviderTest, NoInitConnStatusReadBaseValues) {
+ // Re-initialize the provider, no initial connection status response.
+ Init(false);
+ UmTestUtils::ExpectVariableNotSet(provider_->var_is_connected());
+ UmTestUtils::ExpectVariableNotSet(provider_->var_conn_type());
+ UmTestUtils::ExpectVariableNotSet(provider_->var_conn_last_changed());
+}
+
+// Test that, once a signal is received, the connection status and other info
+// can be read correctly.
+TEST_F(UmRealShillProviderTest, NoInitConnStatusReadConnTypeEthernet) {
+ // Re-initialize the provider, no initial connection status response.
+ Init(false);
+ SetupConnectionAndTestType(kFakeEthernetServicePath,
+ kFakeEthernetServiceProxy,
+ shill::kTypeEthernet,
+ ConnectionType::kEthernet);
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/real_state.h b/update_manager/real_state.h
new file mode 100644
index 0000000..0f74214
--- /dev/null
+++ b/update_manager/real_state.h
@@ -0,0 +1,70 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_STATE_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_STATE_H_
+
+#include <base/memory/scoped_ptr.h>
+
+#include "update_engine/update_manager/state.h"
+
+namespace chromeos_update_manager {
+
+// State concrete implementation.
+class RealState : public State {
+ public:
+ virtual ~RealState() {}
+
+ RealState(ConfigProvider* config_provider,
+ DevicePolicyProvider* device_policy_provider,
+ RandomProvider* random_provider,
+ ShillProvider* shill_provider,
+ SystemProvider* system_provider,
+ TimeProvider* time_provider,
+ UpdaterProvider* updater_provider) :
+ config_provider_(config_provider),
+ device_policy_provider_(device_policy_provider),
+ random_provider_(random_provider),
+ shill_provider_(shill_provider),
+ system_provider_(system_provider),
+ time_provider_(time_provider),
+ updater_provider_(updater_provider) {}
+
+ // These methods return the given provider.
+ virtual ConfigProvider* config_provider() override {
+ return config_provider_.get();
+ }
+ virtual DevicePolicyProvider* device_policy_provider() override {
+ return device_policy_provider_.get();
+ }
+ virtual RandomProvider* random_provider() override {
+ return random_provider_.get();
+ }
+ virtual ShillProvider* shill_provider() override {
+ return shill_provider_.get();
+ }
+ virtual SystemProvider* system_provider() override {
+ return system_provider_.get();
+ }
+ virtual TimeProvider* time_provider() override {
+ return time_provider_.get();
+ }
+ virtual UpdaterProvider* updater_provider() override {
+ return updater_provider_.get();
+ }
+
+ private:
+ // Instances of the providers.
+ scoped_ptr<ConfigProvider> config_provider_;
+ scoped_ptr<DevicePolicyProvider> device_policy_provider_;
+ scoped_ptr<RandomProvider> random_provider_;
+ scoped_ptr<ShillProvider> shill_provider_;
+ scoped_ptr<SystemProvider> system_provider_;
+ scoped_ptr<TimeProvider> time_provider_;
+ scoped_ptr<UpdaterProvider> updater_provider_;
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_STATE_H_
diff --git a/update_manager/real_system_provider.cc b/update_manager/real_system_provider.cc
new file mode 100644
index 0000000..c397027
--- /dev/null
+++ b/update_manager/real_system_provider.cc
@@ -0,0 +1,49 @@
+// 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/update_manager/real_system_provider.h"
+
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/strings/stringprintf.h>
+#include <base/time/time.h>
+#include <vboot/crossystem.h>
+
+#include "update_engine/update_manager/generic_variables.h"
+#include "update_engine/utils.h"
+
+using base::StringPrintf;
+using base::Time;
+using base::TimeDelta;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_manager {
+
+bool RealSystemProvider::Init() {
+ var_is_normal_boot_mode_.reset(
+ new ConstCopyVariable<bool>("is_normal_boot_mode",
+ VbGetSystemPropertyInt("devsw_boot") != 0));
+
+ var_is_official_build_.reset(
+ new ConstCopyVariable<bool>("is_official_build",
+ VbGetSystemPropertyInt("debug_build") == 0));
+
+ var_is_oobe_complete_.reset(
+ new CallCopyVariable<bool>(
+ "is_oobe_complete",
+ base::Bind(&chromeos_update_engine::HardwareInterface::IsOOBEComplete,
+ base::Unretained(hardware_), nullptr)));
+
+ return true;
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/real_system_provider.h b/update_manager/real_system_provider.h
new file mode 100644
index 0000000..b4c401e
--- /dev/null
+++ b/update_manager/real_system_provider.h
@@ -0,0 +1,51 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_SYSTEM_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_SYSTEM_PROVIDER_H_
+
+#include <base/memory/scoped_ptr.h>
+
+#include <string>
+
+#include "update_engine/hardware_interface.h"
+#include "update_engine/update_manager/system_provider.h"
+
+namespace chromeos_update_manager {
+
+// SystemProvider concrete implementation.
+class RealSystemProvider : public SystemProvider {
+ public:
+ explicit RealSystemProvider(
+ chromeos_update_engine::HardwareInterface* hardware)
+ : hardware_(hardware) {}
+
+ // Initializes the provider and returns whether it succeeded.
+ bool Init();
+
+ virtual Variable<bool>* var_is_normal_boot_mode() override {
+ return var_is_normal_boot_mode_.get();
+ }
+
+ virtual Variable<bool>* var_is_official_build() override {
+ return var_is_official_build_.get();
+ }
+
+ virtual Variable<bool>* var_is_oobe_complete() override {
+ return var_is_oobe_complete_.get();
+ }
+
+ private:
+ scoped_ptr<Variable<bool>> var_is_normal_boot_mode_;
+ scoped_ptr<Variable<bool>> var_is_official_build_;
+ scoped_ptr<Variable<bool>> var_is_oobe_complete_;
+
+ chromeos_update_engine::HardwareInterface* hardware_;
+
+ DISALLOW_COPY_AND_ASSIGN(RealSystemProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_SYSTEM_PROVIDER_H_
diff --git a/update_manager/real_system_provider_unittest.cc b/update_manager/real_system_provider_unittest.cc
new file mode 100644
index 0000000..d744671
--- /dev/null
+++ b/update_manager/real_system_provider_unittest.cc
@@ -0,0 +1,43 @@
+// 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/update_manager/real_system_provider.h"
+
+#include <base/memory/scoped_ptr.h>
+#include <base/time/time.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/fake_hardware.h"
+#include "update_engine/update_manager/umtest_utils.h"
+
+namespace chromeos_update_manager {
+
+class UmRealSystemProviderTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ provider_.reset(new RealSystemProvider(&fake_hardware_));
+ EXPECT_TRUE(provider_->Init());
+ }
+
+ chromeos_update_engine::FakeHardware fake_hardware_;
+ scoped_ptr<RealSystemProvider> provider_;
+};
+
+TEST_F(UmRealSystemProviderTest, InitTest) {
+ UMTEST_EXPECT_NOT_NULL(provider_->var_is_normal_boot_mode());
+ UMTEST_EXPECT_NOT_NULL(provider_->var_is_official_build());
+ UMTEST_EXPECT_NOT_NULL(provider_->var_is_oobe_complete());
+}
+
+TEST_F(UmRealSystemProviderTest, IsOOBECompleteTrue) {
+ fake_hardware_.SetIsOOBEComplete(base::Time());
+ UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_oobe_complete());
+}
+
+TEST_F(UmRealSystemProviderTest, IsOOBECompleteFalse) {
+ fake_hardware_.UnsetIsOOBEComplete();
+ UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_oobe_complete());
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/real_time_provider.cc b/update_manager/real_time_provider.cc
new file mode 100644
index 0000000..9067bdd
--- /dev/null
+++ b/update_manager/real_time_provider.cc
@@ -0,0 +1,69 @@
+// 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/update_manager/real_time_provider.h"
+
+#include <base/time/time.h>
+
+#include "update_engine/clock_interface.h"
+
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_engine::ClockInterface;
+using std::string;
+
+namespace chromeos_update_manager {
+
+// A variable returning the current date.
+class CurrDateVariable : public Variable<Time> {
+ public:
+ // TODO(garnold) Turn this into an async variable with the needed callback
+ // logic for when it value changes.
+ CurrDateVariable(const string& name, ClockInterface* clock)
+ : Variable<Time>(name, TimeDelta::FromHours(1)), clock_(clock) {}
+
+ protected:
+ virtual const Time* GetValue(base::TimeDelta /* timeout */,
+ string* /* errmsg */) {
+ Time::Exploded now_exp;
+ clock_->GetWallclockTime().LocalExplode(&now_exp);
+ now_exp.hour = now_exp.minute = now_exp.second = now_exp.millisecond = 0;
+ return new Time(Time::FromLocalExploded(now_exp));
+ }
+
+ private:
+ ClockInterface* clock_;
+
+ DISALLOW_COPY_AND_ASSIGN(CurrDateVariable);
+};
+
+// A variable returning the current hour in local time.
+class CurrHourVariable : public Variable<int> {
+ public:
+ // TODO(garnold) Turn this into an async variable with the needed callback
+ // logic for when it value changes.
+ CurrHourVariable(const string& name, ClockInterface* clock)
+ : Variable<int>(name, TimeDelta::FromMinutes(5)), clock_(clock) {}
+
+ protected:
+ virtual const int* GetValue(base::TimeDelta /* timeout */,
+ string* /* errmsg */) {
+ Time::Exploded exploded;
+ clock_->GetWallclockTime().LocalExplode(&exploded);
+ return new int(exploded.hour);
+ }
+
+ private:
+ ClockInterface* clock_;
+
+ DISALLOW_COPY_AND_ASSIGN(CurrHourVariable);
+};
+
+bool RealTimeProvider::Init() {
+ var_curr_date_.reset(new CurrDateVariable("curr_date", clock_));
+ var_curr_hour_.reset(new CurrHourVariable("curr_hour", clock_));
+ return true;
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/real_time_provider.h b/update_manager/real_time_provider.h
new file mode 100644
index 0000000..ac60336
--- /dev/null
+++ b/update_manager/real_time_provider.h
@@ -0,0 +1,45 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_TIME_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_TIME_PROVIDER_H_
+
+#include <base/memory/scoped_ptr.h>
+#include <base/time/time.h>
+
+#include "update_engine/clock_interface.h"
+#include "update_engine/update_manager/time_provider.h"
+
+namespace chromeos_update_manager {
+
+// TimeProvider concrete implementation.
+class RealTimeProvider : public TimeProvider {
+ public:
+ explicit RealTimeProvider(chromeos_update_engine::ClockInterface* clock)
+ : clock_(clock) {}
+
+ // Initializes the provider and returns whether it succeeded.
+ bool Init();
+
+ virtual Variable<base::Time>* var_curr_date() override {
+ return var_curr_date_.get();
+ }
+
+ virtual Variable<int>* var_curr_hour() override {
+ return var_curr_hour_.get();
+ }
+
+ private:
+ // A clock abstraction (fakeable).
+ chromeos_update_engine::ClockInterface* const clock_;
+
+ scoped_ptr<Variable<base::Time>> var_curr_date_;
+ scoped_ptr<Variable<int>> var_curr_hour_;
+
+ DISALLOW_COPY_AND_ASSIGN(RealTimeProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_TIME_PROVIDER_H_
diff --git a/update_manager/real_time_provider_unittest.cc b/update_manager/real_time_provider_unittest.cc
new file mode 100644
index 0000000..6b236db
--- /dev/null
+++ b/update_manager/real_time_provider_unittest.cc
@@ -0,0 +1,70 @@
+// 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 <base/logging.h>
+#include <base/memory/scoped_ptr.h>
+#include <base/time/time.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/fake_clock.h"
+#include "update_engine/update_manager/real_time_provider.h"
+#include "update_engine/update_manager/umtest_utils.h"
+
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_engine::FakeClock;
+
+namespace chromeos_update_manager {
+
+class UmRealTimeProviderTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ // The provider initializes correctly.
+ provider_.reset(new RealTimeProvider(&fake_clock_));
+ UMTEST_ASSERT_NOT_NULL(provider_.get());
+ ASSERT_TRUE(provider_->Init());
+ }
+
+ // Generates a fixed timestamp for use in faking the current time.
+ Time CurrTime() {
+ Time::Exploded now_exp;
+ now_exp.year = 2014;
+ now_exp.month = 3;
+ now_exp.day_of_week = 2;
+ now_exp.day_of_month = 18;
+ now_exp.hour = 8;
+ now_exp.minute = 5;
+ now_exp.second = 33;
+ now_exp.millisecond = 675;
+ return Time::FromLocalExploded(now_exp);
+ }
+
+ FakeClock fake_clock_;
+ scoped_ptr<RealTimeProvider> provider_;
+};
+
+TEST_F(UmRealTimeProviderTest, CurrDateValid) {
+ const Time now = CurrTime();
+ Time::Exploded exploded;
+ now.LocalExplode(&exploded);
+ exploded.hour = 0;
+ exploded.minute = 0;
+ exploded.second = 0;
+ exploded.millisecond = 0;
+ const Time expected = Time::FromLocalExploded(exploded);
+
+ fake_clock_.SetWallclockTime(now);
+ UmTestUtils::ExpectVariableHasValue(expected, provider_->var_curr_date());
+}
+
+TEST_F(UmRealTimeProviderTest, CurrHourValid) {
+ const Time now = CurrTime();
+ Time::Exploded expected;
+ now.LocalExplode(&expected);
+ fake_clock_.SetWallclockTime(now);
+ UmTestUtils::ExpectVariableHasValue(expected.hour,
+ provider_->var_curr_hour());
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/real_updater_provider.cc b/update_manager/real_updater_provider.cc
new file mode 100644
index 0000000..7863019
--- /dev/null
+++ b/update_manager/real_updater_provider.cc
@@ -0,0 +1,358 @@
+// 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/update_manager/real_updater_provider.h"
+
+#include <inttypes.h>
+
+#include <string>
+
+#include <base/strings/stringprintf.h>
+#include <base/time/time.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_update_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<int64_t> {
+ public:
+ using UpdaterVariableBase<int64_t>::UpdaterVariableBase;
+
+ private:
+ virtual const int64_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 int64_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);
+};
+
+// A variable returning the number of consecutive failed update checks.
+class ConsecutiveFailedUpdateChecksVariable :
+ public UpdaterVariableBase<unsigned int> {
+ public:
+ using UpdaterVariableBase<unsigned int>::UpdaterVariableBase;
+
+ private:
+ virtual const unsigned int* GetValue(TimeDelta /* timeout */,
+ string* /* errmsg */) override {
+ return new unsigned int(
+ system_state()->update_attempter()->consecutive_failed_update_checks());
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ConsecutiveFailedUpdateChecksVariable);
+};
+
+// RealUpdaterProvider methods.
+
+RealUpdaterProvider::RealUpdaterProvider(SystemState* system_state)
+ : system_state_(system_state),
+ var_updater_started_time_("updater_started_time",
+ system_state->clock()->GetWallclockTime()),
+ 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)),
+ var_consecutive_failed_update_checks_(
+ new ConsecutiveFailedUpdateChecksVariable(
+ "consecutive_failed_update_checks", system_state_)) {}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/real_updater_provider.h b/update_manager/real_updater_provider.h
new file mode 100644
index 0000000..d7e3f06
--- /dev/null
+++ b/update_manager/real_updater_provider.h
@@ -0,0 +1,102 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_UPDATER_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_UPDATER_PROVIDER_H_
+
+#include <base/memory/scoped_ptr.h>
+
+#include "update_engine/system_state.h"
+#include "update_engine/update_manager/generic_variables.h"
+#include "update_engine/update_manager/updater_provider.h"
+
+namespace chromeos_update_manager {
+
+// A concrete UpdaterProvider implementation using local (in-process) bindings.
+class RealUpdaterProvider : public UpdaterProvider {
+ public:
+ // We assume that any other object handle we get from the system state is
+ // "volatile", and so must be re-acquired whenever access is needed; this
+ // guarantees that parts of the system state can be mocked out at any time
+ // during testing. We further assume that, by the time Init() is called, the
+ // system state object is fully populated and usable.
+ explicit RealUpdaterProvider(
+ chromeos_update_engine::SystemState* system_state);
+
+ // Initializes the provider and returns whether it succeeded.
+ bool Init() { return true; }
+
+ virtual Variable<base::Time>* var_updater_started_time() override {
+ return &var_updater_started_time_;
+ }
+
+ virtual Variable<base::Time>* var_last_checked_time() override {
+ return var_last_checked_time_.get();
+ }
+
+ virtual Variable<base::Time>* var_update_completed_time() override {
+ return var_update_completed_time_.get();
+ }
+
+ virtual Variable<double>* var_progress() override {
+ return var_progress_.get();
+ }
+
+ virtual Variable<Stage>* var_stage() override {
+ return var_stage_.get();
+ }
+
+ virtual Variable<std::string>* var_new_version() override {
+ return var_new_version_.get();
+ }
+
+ virtual Variable<int64_t>* var_payload_size() override {
+ return var_payload_size_.get();
+ }
+
+ virtual Variable<std::string>* var_curr_channel() override {
+ return var_curr_channel_.get();
+ }
+
+ virtual Variable<std::string>* var_new_channel() override {
+ return var_new_channel_.get();
+ }
+
+ virtual Variable<bool>* var_p2p_enabled() override {
+ return var_p2p_enabled_.get();
+ }
+
+ virtual Variable<bool>* var_cellular_enabled() override {
+ return var_cellular_enabled_.get();
+ }
+
+ virtual Variable<unsigned int>*
+ var_consecutive_failed_update_checks() override {
+ return var_consecutive_failed_update_checks_.get();
+ }
+
+ private:
+ // A pointer to the update engine's system state aggregator.
+ chromeos_update_engine::SystemState* system_state_;
+
+ // Variable implementations.
+ ConstCopyVariable<base::Time> var_updater_started_time_;
+ scoped_ptr<Variable<base::Time>> var_last_checked_time_;
+ scoped_ptr<Variable<base::Time>> var_update_completed_time_;
+ scoped_ptr<Variable<double>> var_progress_;
+ scoped_ptr<Variable<Stage>> var_stage_;
+ scoped_ptr<Variable<std::string>> var_new_version_;
+ scoped_ptr<Variable<int64_t>> var_payload_size_;
+ scoped_ptr<Variable<std::string>> var_curr_channel_;
+ scoped_ptr<Variable<std::string>> var_new_channel_;
+ scoped_ptr<Variable<bool>> var_p2p_enabled_;
+ scoped_ptr<Variable<bool>> var_cellular_enabled_;
+ scoped_ptr<Variable<unsigned int>> var_consecutive_failed_update_checks_;
+
+ DISALLOW_COPY_AND_ASSIGN(RealUpdaterProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_REAL_UPDATER_PROVIDER_H_
diff --git a/update_manager/real_updater_provider_unittest.cc b/update_manager/real_updater_provider_unittest.cc
new file mode 100644
index 0000000..7bea777
--- /dev/null
+++ b/update_manager/real_updater_provider_unittest.cc
@@ -0,0 +1,457 @@
+// 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/update_manager/real_updater_provider.h"
+
+#include <string>
+
+#include <base/memory/scoped_ptr.h>
+#include <base/time/time.h>
+#include <chromeos/dbus/service_constants.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/fake_clock.h"
+#include "update_engine/fake_system_state.h"
+#include "update_engine/omaha_request_params.h"
+#include "update_engine/prefs_mock.h"
+#include "update_engine/update_attempter_mock.h"
+#include "update_engine/update_manager/umtest_utils.h"
+
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_engine::FakeClock;
+using chromeos_update_engine::FakeSystemState;
+using chromeos_update_engine::OmahaRequestParams;
+using chromeos_update_engine::PrefsMock;
+using chromeos_update_engine::UpdateAttempterMock;
+using std::string;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::StrEq;
+using testing::_;
+
+namespace {
+
+// Generates a fixed timestamp for use in faking the current time.
+Time FixedTime() {
+ Time::Exploded now_exp;
+ now_exp.year = 2014;
+ now_exp.month = 3;
+ now_exp.day_of_week = 2;
+ now_exp.day_of_month = 18;
+ now_exp.hour = 8;
+ now_exp.minute = 5;
+ now_exp.second = 33;
+ now_exp.millisecond = 675;
+ return Time::FromLocalExploded(now_exp);
+}
+
+// Rounds down a timestamp to the nearest second. This is useful when faking
+// times that are converted to time_t (no sub-second resolution).
+Time RoundedToSecond(Time time) {
+ Time::Exploded exp;
+ time.LocalExplode(&exp);
+ exp.millisecond = 0;
+ return Time::FromLocalExploded(exp);
+}
+
+} // namespace
+
+namespace chromeos_update_manager {
+
+class UmRealUpdaterProviderTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ fake_clock_ = fake_sys_state_.fake_clock();
+ provider_.reset(new RealUpdaterProvider(&fake_sys_state_));
+ UMTEST_ASSERT_NOT_NULL(provider_.get());
+ // Check that provider initializes corrrectly.
+ ASSERT_TRUE(provider_->Init());
+ }
+
+ // Sets up mock expectations for testing a variable that reads a Boolean pref
+ // |key|. |key_exists| determines whether the key is present. If it is, then
+ // |get_boolean_success| determines whether reading it is successful, and if
+ // so |output| is the value being read.
+ void SetupReadBooleanPref(const char* key, bool key_exists,
+ bool get_boolean_success, bool output) {
+ PrefsMock* const mock_prefs = fake_sys_state_.mock_prefs();
+ EXPECT_CALL(*mock_prefs, Exists(StrEq(key))).WillOnce(Return(key_exists));
+ if (key_exists) {
+ auto& get_boolean = EXPECT_CALL(
+ *fake_sys_state_.mock_prefs(), GetBoolean(StrEq(key), _));
+ if (get_boolean_success)
+ get_boolean.WillOnce(DoAll(SetArgPointee<1>(output), Return(true)));
+ else
+ get_boolean.WillOnce(Return(false));
+ }
+ }
+
+ // Sets up mock expectations for testing the update completed time reporting.
+ // |valid| determines whether the returned time is valid. Returns the expected
+ // update completed time value.
+ Time SetupUpdateCompletedTime(bool valid) {
+ const TimeDelta kDurationSinceUpdate = TimeDelta::FromMinutes(7);
+ const Time kUpdateBootTime = Time() + kDurationSinceUpdate * 2;
+ const Time kCurrBootTime = (valid ?
+ kUpdateBootTime + kDurationSinceUpdate :
+ kUpdateBootTime - kDurationSinceUpdate);
+ const Time kCurrWallclockTime = FixedTime();
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetBootTimeAtUpdate(_))
+ .WillOnce(DoAll(SetArgPointee<0>(kUpdateBootTime), Return(true)));
+ fake_clock_->SetBootTime(kCurrBootTime);
+ fake_clock_->SetWallclockTime(kCurrWallclockTime);
+ return kCurrWallclockTime - kDurationSinceUpdate;
+ }
+
+ FakeSystemState fake_sys_state_;
+ FakeClock* fake_clock_; // Short for fake_sys_state_.fake_clock()
+ scoped_ptr<RealUpdaterProvider> provider_;
+};
+
+TEST_F(UmRealUpdaterProviderTest, UpdaterStartedTimeIsWallclockTime) {
+ fake_clock_->SetWallclockTime(Time::FromDoubleT(123.456));
+ fake_clock_->SetMonotonicTime(Time::FromDoubleT(456.123));
+ // Run SetUp again to re-setup the provider under test to use these values.
+ SetUp();
+ UmTestUtils::ExpectVariableHasValue(Time::FromDoubleT(123.456),
+ provider_->var_updater_started_time());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetLastCheckedTimeOkay) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<0>(FixedTime().ToTimeT()), Return(true)));
+ UmTestUtils::ExpectVariableHasValue(RoundedToSecond(FixedTime()),
+ provider_->var_last_checked_time());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetLastCheckedTimeFailNoValue) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(Return(false));
+ UmTestUtils::ExpectVariableNotSet(provider_->var_last_checked_time());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetProgressOkayMin) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(0.0), Return(true)));
+ UmTestUtils::ExpectVariableHasValue(0.0, provider_->var_progress());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetProgressOkayMid) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(0.3), Return(true)));
+ UmTestUtils::ExpectVariableHasValue(0.3, provider_->var_progress());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetProgressOkayMax) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(1.0), Return(true)));
+ UmTestUtils::ExpectVariableHasValue(1.0, provider_->var_progress());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetProgressFailNoValue) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(Return(false));
+ UmTestUtils::ExpectVariableNotSet(provider_->var_progress());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetProgressFailTooSmall) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(-2.0), Return(true)));
+ UmTestUtils::ExpectVariableNotSet(provider_->var_progress());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetProgressFailTooBig) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(2.0), Return(true)));
+ UmTestUtils::ExpectVariableNotSet(provider_->var_progress());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageOkayIdle) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<2>(update_engine::kUpdateStatusIdle),
+ Return(true)));
+ UmTestUtils::ExpectVariableHasValue(Stage::kIdle, provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageOkayCheckingForUpdate) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(
+ SetArgPointee<2>(update_engine::kUpdateStatusCheckingForUpdate),
+ Return(true)));
+ UmTestUtils::ExpectVariableHasValue(Stage::kCheckingForUpdate,
+ provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageOkayUpdateAvailable) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(
+ SetArgPointee<2>(update_engine::kUpdateStatusUpdateAvailable),
+ Return(true)));
+ UmTestUtils::ExpectVariableHasValue(Stage::kUpdateAvailable,
+ provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageOkayDownloading) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<2>(update_engine::kUpdateStatusDownloading),
+ Return(true)));
+ UmTestUtils::ExpectVariableHasValue(Stage::kDownloading,
+ provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageOkayVerifying) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<2>(update_engine::kUpdateStatusVerifying),
+ Return(true)));
+ UmTestUtils::ExpectVariableHasValue(Stage::kVerifying,
+ provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageOkayFinalizing) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<2>(update_engine::kUpdateStatusFinalizing),
+ Return(true)));
+ UmTestUtils::ExpectVariableHasValue(Stage::kFinalizing,
+ provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageOkayUpdatedNeedReboot) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(
+ SetArgPointee<2>(update_engine::kUpdateStatusUpdatedNeedReboot),
+ Return(true)));
+ UmTestUtils::ExpectVariableHasValue(Stage::kUpdatedNeedReboot,
+ provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageOkayReportingErrorEvent) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(
+ SetArgPointee<2>(update_engine::kUpdateStatusReportingErrorEvent),
+ Return(true)));
+ UmTestUtils::ExpectVariableHasValue(Stage::kReportingErrorEvent,
+ provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageOkayAttemptingRollback) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(
+ SetArgPointee<2>(update_engine::kUpdateStatusAttemptingRollback),
+ Return(true)));
+ UmTestUtils::ExpectVariableHasValue(Stage::kAttemptingRollback,
+ provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageFailNoValue) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(Return(false));
+ UmTestUtils::ExpectVariableNotSet(provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageFailUnknown) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<2>("FooUpdateEngineState"),
+ Return(true)));
+ UmTestUtils::ExpectVariableNotSet(provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetStageFailEmpty) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<2>(""), Return(true)));
+ UmTestUtils::ExpectVariableNotSet(provider_->var_stage());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetNewVersionOkay) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<3>("1.2.0"), Return(true)));
+ UmTestUtils::ExpectVariableHasValue(string("1.2.0"),
+ provider_->var_new_version());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetNewVersionFailNoValue) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(Return(false));
+ UmTestUtils::ExpectVariableNotSet(provider_->var_new_version());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeOkayZero) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(static_cast<int64_t>(0)), Return(true)));
+ UmTestUtils::ExpectVariableHasValue(static_cast<int64_t>(0),
+ provider_->var_payload_size());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeOkayArbitrary) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(static_cast<int64_t>(567890)),
+ Return(true)));
+ UmTestUtils::ExpectVariableHasValue(static_cast<int64_t>(567890),
+ provider_->var_payload_size());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeOkayTwoGigabytes) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(static_cast<int64_t>(1) << 31),
+ Return(true)));
+ UmTestUtils::ExpectVariableHasValue(static_cast<int64_t>(1) << 31,
+ provider_->var_payload_size());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeFailNoValue) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(Return(false));
+ UmTestUtils::ExpectVariableNotSet(provider_->var_payload_size());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetPayloadSizeFailNegative) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ GetStatus(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(static_cast<int64_t>(-1024)),
+ Return(true)));
+ UmTestUtils::ExpectVariableNotSet(provider_->var_payload_size());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetCurrChannelOkay) {
+ const string kChannelName("foo-channel");
+ OmahaRequestParams request_params(&fake_sys_state_);
+ request_params.Init("", "", false);
+ request_params.set_current_channel(kChannelName);
+ fake_sys_state_.set_request_params(&request_params);
+ UmTestUtils::ExpectVariableHasValue(kChannelName,
+ provider_->var_curr_channel());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetCurrChannelFailEmpty) {
+ OmahaRequestParams request_params(&fake_sys_state_);
+ request_params.Init("", "", false);
+ request_params.set_current_channel("");
+ fake_sys_state_.set_request_params(&request_params);
+ UmTestUtils::ExpectVariableNotSet(provider_->var_curr_channel());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetNewChannelOkay) {
+ const string kChannelName("foo-channel");
+ OmahaRequestParams request_params(&fake_sys_state_);
+ request_params.Init("", "", false);
+ request_params.set_target_channel(kChannelName);
+ fake_sys_state_.set_request_params(&request_params);
+ UmTestUtils::ExpectVariableHasValue(kChannelName,
+ provider_->var_new_channel());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetNewChannelFailEmpty) {
+ OmahaRequestParams request_params(&fake_sys_state_);
+ request_params.Init("", "", false);
+ request_params.set_target_channel("");
+ fake_sys_state_.set_request_params(&request_params);
+ UmTestUtils::ExpectVariableNotSet(provider_->var_new_channel());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetP2PEnabledOkayPrefDoesntExist) {
+ SetupReadBooleanPref(chromeos_update_engine::kPrefsP2PEnabled,
+ false, false, false);
+ UmTestUtils::ExpectVariableHasValue(false, provider_->var_p2p_enabled());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetP2PEnabledOkayPrefReadsFalse) {
+ SetupReadBooleanPref(chromeos_update_engine::kPrefsP2PEnabled,
+ true, true, false);
+ UmTestUtils::ExpectVariableHasValue(false, provider_->var_p2p_enabled());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetP2PEnabledOkayPrefReadsTrue) {
+ SetupReadBooleanPref(chromeos_update_engine::kPrefsP2PEnabled,
+ true, true, true);
+ UmTestUtils::ExpectVariableHasValue(true, provider_->var_p2p_enabled());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetP2PEnabledFailCannotReadPref) {
+ SetupReadBooleanPref(chromeos_update_engine::kPrefsP2PEnabled,
+ true, false, false);
+ UmTestUtils::ExpectVariableNotSet(provider_->var_p2p_enabled());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetCellularEnabledOkayPrefDoesntExist) {
+ SetupReadBooleanPref(
+ chromeos_update_engine::kPrefsUpdateOverCellularPermission,
+ false, false, false);
+ UmTestUtils::ExpectVariableHasValue(false, provider_->var_cellular_enabled());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetCellularEnabledOkayPrefReadsFalse) {
+ SetupReadBooleanPref(
+ chromeos_update_engine::kPrefsUpdateOverCellularPermission,
+ true, true, false);
+ UmTestUtils::ExpectVariableHasValue(false, provider_->var_cellular_enabled());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetCellularEnabledOkayPrefReadsTrue) {
+ SetupReadBooleanPref(
+ chromeos_update_engine::kPrefsUpdateOverCellularPermission,
+ true, true, true);
+ UmTestUtils::ExpectVariableHasValue(true, provider_->var_cellular_enabled());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetCellularEnabledFailCannotReadPref) {
+ SetupReadBooleanPref(
+ chromeos_update_engine::kPrefsUpdateOverCellularPermission,
+ true, false, false);
+ UmTestUtils::ExpectVariableNotSet(provider_->var_cellular_enabled());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetUpdateCompletedTimeOkay) {
+ Time expected = SetupUpdateCompletedTime(true);
+ UmTestUtils::ExpectVariableHasValue(expected,
+ provider_->var_update_completed_time());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetUpdateCompletedTimeFailNoValue) {
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(), GetBootTimeAtUpdate(_))
+ .WillOnce(Return(false));
+ UmTestUtils::ExpectVariableNotSet(provider_->var_update_completed_time());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetUpdateCompletedTimeFailInvalidValue) {
+ SetupUpdateCompletedTime(false);
+ UmTestUtils::ExpectVariableNotSet(provider_->var_update_completed_time());
+}
+
+TEST_F(UmRealUpdaterProviderTest, GetConsecutiveFailedUpdateChecks) {
+ const unsigned int kNumFailedChecks = 3;
+ EXPECT_CALL(*fake_sys_state_.mock_update_attempter(),
+ consecutive_failed_update_checks())
+ .WillRepeatedly(Return(kNumFailedChecks));
+ UmTestUtils::ExpectVariableHasValue(
+ kNumFailedChecks, provider_->var_consecutive_failed_update_checks());
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/shill_provider.h b/update_manager/shill_provider.h
new file mode 100644
index 0000000..51180fa
--- /dev/null
+++ b/update_manager/shill_provider.h
@@ -0,0 +1,60 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_SHILL_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_SHILL_PROVIDER_H_
+
+#include <base/time/time.h>
+
+#include "update_engine/update_manager/provider.h"
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+enum class ConnectionType {
+ kEthernet,
+ kWifi,
+ kWimax,
+ kBluetooth,
+ kCellular,
+ kUnknown
+};
+
+enum class ConnectionTethering {
+ kNotDetected,
+ kSuspected,
+ kConfirmed,
+ kUnknown,
+};
+
+// Provider for networking related information.
+class ShillProvider : public Provider {
+ public:
+ virtual ~ShillProvider() {}
+
+ // A variable returning whether we currently have network connectivity.
+ virtual Variable<bool>* var_is_connected() = 0;
+
+ // A variable returning the current network connection type. Unknown if not
+ // connected.
+ virtual Variable<ConnectionType>* var_conn_type() = 0;
+
+ // A variable returning the tethering mode of a network connection. Unknown if
+ // not connected.
+ virtual Variable<ConnectionTethering>* var_conn_tethering() = 0;
+
+ // A variable retruning the time when network connection last changed.
+ // Initialized to current time.
+ virtual Variable<base::Time>* var_conn_last_changed() = 0;
+
+ protected:
+ ShillProvider() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ShillProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_SHILL_PROVIDER_H_
diff --git a/update_manager/state.h b/update_manager/state.h
new file mode 100644
index 0000000..0fe4116
--- /dev/null
+++ b/update_manager/state.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_STATE_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_STATE_H_
+
+#include "update_engine/update_manager/config_provider.h"
+#include "update_engine/update_manager/device_policy_provider.h"
+#include "update_engine/update_manager/random_provider.h"
+#include "update_engine/update_manager/shill_provider.h"
+#include "update_engine/update_manager/system_provider.h"
+#include "update_engine/update_manager/time_provider.h"
+#include "update_engine/update_manager/updater_provider.h"
+
+namespace chromeos_update_manager {
+
+// The State class is an interface to the ensemble of providers. This class
+// gives visibility of the state providers to policy implementations.
+class State {
+ public:
+ virtual ~State() {}
+
+ // These methods return the given provider.
+ virtual ConfigProvider* config_provider() = 0;
+ virtual DevicePolicyProvider* device_policy_provider() = 0;
+ virtual RandomProvider* random_provider() = 0;
+ virtual ShillProvider* shill_provider() = 0;
+ virtual SystemProvider* system_provider() = 0;
+ virtual TimeProvider* time_provider() = 0;
+ virtual UpdaterProvider* updater_provider() = 0;
+
+ protected:
+ State() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(State);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_STATE_H_
diff --git a/update_manager/state_factory.cc b/update_manager/state_factory.cc
new file mode 100644
index 0000000..6aa1761
--- /dev/null
+++ b/update_manager/state_factory.cc
@@ -0,0 +1,59 @@
+// 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/update_manager/state_factory.h"
+
+#include <base/logging.h>
+#include <base/memory/scoped_ptr.h>
+
+#include "update_engine/clock_interface.h"
+#include "update_engine/update_manager/real_config_provider.h"
+#include "update_engine/update_manager/real_device_policy_provider.h"
+#include "update_engine/update_manager/real_random_provider.h"
+#include "update_engine/update_manager/real_shill_provider.h"
+#include "update_engine/update_manager/real_state.h"
+#include "update_engine/update_manager/real_system_provider.h"
+#include "update_engine/update_manager/real_time_provider.h"
+#include "update_engine/update_manager/real_updater_provider.h"
+
+namespace chromeos_update_manager {
+
+State* DefaultStateFactory(policy::PolicyProvider* policy_provider,
+ chromeos_update_engine::DBusWrapperInterface* dbus,
+ chromeos_update_engine::SystemState* system_state) {
+ chromeos_update_engine::ClockInterface* const clock = system_state->clock();
+ scoped_ptr<RealConfigProvider> config_provider(
+ new RealConfigProvider(system_state->hardware()));
+ scoped_ptr<RealDevicePolicyProvider> device_policy_provider(
+ new RealDevicePolicyProvider(policy_provider));
+ scoped_ptr<RealRandomProvider> random_provider(new RealRandomProvider());
+ scoped_ptr<RealShillProvider> shill_provider(
+ new RealShillProvider(dbus, clock));
+ scoped_ptr<RealSystemProvider> system_provider(
+ new RealSystemProvider(system_state->hardware()));
+ scoped_ptr<RealTimeProvider> time_provider(new RealTimeProvider(clock));
+ scoped_ptr<RealUpdaterProvider> updater_provider(
+ new RealUpdaterProvider(system_state));
+
+ if (!(config_provider->Init() &&
+ device_policy_provider->Init() &&
+ random_provider->Init() &&
+ shill_provider->Init() &&
+ system_provider->Init() &&
+ time_provider->Init() &&
+ updater_provider->Init())) {
+ LOG(ERROR) << "Error initializing providers";
+ return NULL;
+ }
+
+ return new RealState(config_provider.release(),
+ device_policy_provider.release(),
+ random_provider.release(),
+ shill_provider.release(),
+ system_provider.release(),
+ time_provider.release(),
+ updater_provider.release());
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/state_factory.h b/update_manager/state_factory.h
new file mode 100644
index 0000000..8e97554
--- /dev/null
+++ b/update_manager/state_factory.h
@@ -0,0 +1,26 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_STATE_FACTORY_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_STATE_FACTORY_H_
+
+#include "update_engine/dbus_wrapper_interface.h"
+#include "update_engine/system_state.h"
+#include "update_engine/update_manager/state.h"
+
+namespace chromeos_update_manager {
+
+// Creates and initializes a new UpdateManager State instance containing real
+// providers instantiated using the passed interfaces. The State doesn't take
+// ownership of the passed interfaces, which need to remain available during the
+// life of this instance. Returns null if one of the underlying providers fails
+// to initialize.
+State* DefaultStateFactory(
+ policy::PolicyProvider* policy_provider,
+ chromeos_update_engine::DBusWrapperInterface* dbus,
+ chromeos_update_engine::SystemState* system_state);
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_STATE_FACTORY_H_
diff --git a/update_manager/system_provider.h b/update_manager/system_provider.h
new file mode 100644
index 0000000..831f377
--- /dev/null
+++ b/update_manager/system_provider.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_SYSTEM_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_SYSTEM_PROVIDER_H_
+
+#include "update_engine/update_manager/provider.h"
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+// Provider for system information, mostly constant, such as the information
+// reported by crossystem, the kernel boot command line and the partition table.
+class SystemProvider : public Provider {
+ public:
+ virtual ~SystemProvider() {}
+
+ // Returns true if the boot mode is normal or if it's unable to
+ // determine the boot mode. Returns false if the boot mode is
+ // developer.
+ virtual Variable<bool>* var_is_normal_boot_mode() = 0;
+
+ // Returns whether this is an official Chrome OS build.
+ virtual Variable<bool>* var_is_official_build() = 0;
+
+ // Returns a variable that tells whether OOBE was completed.
+ virtual Variable<bool>* var_is_oobe_complete() = 0;
+
+ protected:
+ SystemProvider() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SystemProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_SYSTEM_PROVIDER_H_
diff --git a/update_manager/time_provider.h b/update_manager/time_provider.h
new file mode 100644
index 0000000..146d250
--- /dev/null
+++ b/update_manager/time_provider.h
@@ -0,0 +1,36 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_TIME_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_TIME_PROVIDER_H_
+
+#include <base/time/time.h>
+
+#include "update_engine/update_manager/provider.h"
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+// Provider for time related information.
+class TimeProvider : public Provider {
+ public:
+ virtual ~TimeProvider() {}
+
+ // Returns the current date. The time of day component will be zero.
+ virtual Variable<base::Time>* var_curr_date() = 0;
+
+ // Returns the current hour (0 to 23) in local time. The type is int to keep
+ // consistent with base::Time.
+ virtual Variable<int>* var_curr_hour() = 0;
+
+ protected:
+ TimeProvider() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TimeProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_TIME_PROVIDER_H_
diff --git a/update_manager/umtest_utils.cc b/update_manager/umtest_utils.cc
new file mode 100644
index 0000000..41dc0a6
--- /dev/null
+++ b/update_manager/umtest_utils.cc
@@ -0,0 +1,17 @@
+// 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 <base/time/time.h>
+
+#include "update_engine/update_manager/umtest_utils.h"
+
+namespace chromeos_update_manager {
+
+const unsigned UmTestUtils::kDefaultTimeoutInSeconds = 1;
+
+void PrintTo(const EvalStatus& status, ::std::ostream* os) {
+ *os << ToString(status);
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/umtest_utils.h b/update_manager/umtest_utils.h
new file mode 100644
index 0000000..bd8da8c
--- /dev/null
+++ b/update_manager/umtest_utils.h
@@ -0,0 +1,70 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_UMTEST_UTILS_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_UMTEST_UTILS_H_
+
+#include <iostream>
+
+#include <base/memory/scoped_ptr.h>
+#include <base/time/time.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/update_manager/policy.h"
+#include "update_engine/update_manager/variable.h"
+
+// Convenience macros for checking null-ness of pointers.
+//
+// Purportedly, gtest should support pointer comparison when the first argument
+// is a pointer (e.g. NULL). It is therefore appropriate to use
+// {ASSERT,EXPECT}_{EQ,NE} for our purposes. However, gtest fails to realize
+// that NULL is a pointer when used with the _NE variants, unless we explicitly
+// cast it to a pointer type, and so we inject this casting.
+//
+// Note that checking nullness of the content of a scoped_ptr requires getting
+// the inner pointer value via get().
+#define UMTEST_ASSERT_NULL(p) ASSERT_EQ(NULL, p)
+#define UMTEST_ASSERT_NOT_NULL(p) ASSERT_NE(reinterpret_cast<void*>(NULL), p)
+#define UMTEST_EXPECT_NULL(p) EXPECT_EQ(NULL, p)
+#define UMTEST_EXPECT_NOT_NULL(p) EXPECT_NE(reinterpret_cast<void*>(NULL), p)
+
+
+namespace chromeos_update_manager {
+
+// A help class with common functionality for use in Update Manager testing.
+class UmTestUtils {
+ public:
+ // A default timeout to use when making various queries.
+ static const base::TimeDelta DefaultTimeout() {
+ return base::TimeDelta::FromSeconds(kDefaultTimeoutInSeconds);
+ }
+
+ // Calls GetValue on |variable| and expects its result to be |expected|.
+ template<typename T>
+ static void ExpectVariableHasValue(const T& expected, Variable<T>* variable) {
+ UMTEST_ASSERT_NOT_NULL(variable);
+ scoped_ptr<const T> value(variable->GetValue(DefaultTimeout(), nullptr));
+ UMTEST_ASSERT_NOT_NULL(value.get()) << "Variable: " << variable->GetName();
+ EXPECT_EQ(expected, *value) << "Variable: " << variable->GetName();
+ }
+
+ // Calls GetValue on |variable| and expects its result to be null.
+ template<typename T>
+ static void ExpectVariableNotSet(Variable<T>* variable) {
+ UMTEST_ASSERT_NOT_NULL(variable);
+ scoped_ptr<const T> value(variable->GetValue(DefaultTimeout(), nullptr));
+ UMTEST_EXPECT_NULL(value.get()) << "Variable: " << variable->GetName();
+ }
+
+ private:
+ static const unsigned kDefaultTimeoutInSeconds;
+};
+
+// PrintTo() functions are used by gtest to print these values. They need to be
+// defined on the same namespace where the type was defined.
+void PrintTo(const EvalStatus& status, ::std::ostream* os);
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_UMTEST_UTILS_H_
diff --git a/update_manager/update_manager-inl.h b/update_manager/update_manager-inl.h
new file mode 100644
index 0000000..1b1dc49
--- /dev/null
+++ b/update_manager/update_manager-inl.h
@@ -0,0 +1,111 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_MANAGER_INL_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_MANAGER_INL_H_
+
+#include <string>
+
+#include <base/bind.h>
+
+#include "update_engine/update_manager/evaluation_context.h"
+#include "update_engine/update_manager/event_loop.h"
+
+namespace chromeos_update_manager {
+
+template<typename R, typename... Args>
+EvalStatus UpdateManager::EvaluatePolicy(
+ EvaluationContext* ec,
+ EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
+ std::string*, R*,
+ Args...) const,
+ R* result, Args... args) {
+ std::string error;
+
+ // First try calling the actual policy.
+ EvalStatus status = (policy_.get()->*policy_method)(ec, state_.get(), &error,
+ result, args...);
+
+ if (status == EvalStatus::kFailed) {
+ LOG(WARNING) << "PolicyRequest() failed with error: " << error;
+ error.clear();
+ status = (default_policy_.*policy_method)(ec, state_.get(), &error,
+ result, args...);
+
+ if (status == EvalStatus::kFailed) {
+ LOG(WARNING) << "Request to DefaultPolicy also failed, passing error.";
+ }
+ }
+ // TODO(deymo): Log the actual state used from the EvaluationContext.
+ return status;
+}
+
+template<typename R, typename... Args>
+void UpdateManager::OnPolicyReadyToEvaluate(
+ scoped_refptr<EvaluationContext> ec,
+ base::Callback<void(EvalStatus status, const R& result)> callback,
+ EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
+ std::string*, R*,
+ Args...) const,
+ Args... args) {
+ ec->ResetEvaluation();
+ R result;
+ EvalStatus status = EvaluatePolicy(ec, policy_method, &result, args...);
+
+ if (status != EvalStatus::kAskMeAgainLater) {
+ // AsyncPolicyRequest finished.
+ callback.Run(status, result);
+ return;
+ }
+ // Re-schedule the policy request based on used variables.
+ base::Closure closure = base::Bind(
+ &UpdateManager::OnPolicyReadyToEvaluate<R, Args...>,
+ base::Unretained(this), ec, callback, policy_method, args...);
+
+ if (!ec->RunOnValueChangeOrTimeout(closure)) {
+ // The policy method didn't use any non-const variable nor there's any
+ // time-based event that will change the status of evaluation. We call the
+ // callback with EvalStatus::kAskMeAgainLater.
+ LOG(ERROR) << "Policy implementation didn't use any non-const variable "
+ "but returned kAskMeAgainLater.";
+ callback.Run(EvalStatus::kAskMeAgainLater, result);
+ return;
+ }
+}
+
+template<typename R, typename... ActualArgs, typename... ExpectedArgs>
+EvalStatus UpdateManager::PolicyRequest(
+ EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
+ std::string*, R*,
+ ExpectedArgs...) const,
+ R* result, ActualArgs... args) {
+ scoped_refptr<EvaluationContext> ec(new EvaluationContext(clock_));
+ // A PolicyRequest allways consists on a single evaluation on a new
+ // EvaluationContext.
+ // IMPORTANT: To ensure that ActualArgs can be converted to ExpectedArgs, we
+ // explicitly instantiate EvaluatePolicy with the latter in lieu of the
+ // former.
+ return EvaluatePolicy<R, ExpectedArgs...>(ec, policy_method, result, args...);
+}
+
+template<typename R, typename... ActualArgs, typename... ExpectedArgs>
+void UpdateManager::AsyncPolicyRequest(
+ base::Callback<void(EvalStatus, const R& result)> callback,
+ EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
+ std::string*, R*,
+ ExpectedArgs...) const,
+ ActualArgs... args) {
+ scoped_refptr<EvaluationContext> ec = new EvaluationContext(clock_);
+ // IMPORTANT: To ensure that ActualArgs can be converted to ExpectedArgs, we
+ // explicitly instantiate UpdateManager::OnPolicyReadyToEvaluate with the
+ // latter in lieu of the former.
+ base::Closure closure = base::Bind(
+ &UpdateManager::OnPolicyReadyToEvaluate<R, ExpectedArgs...>,
+ base::Unretained(this), ec, callback, policy_method, args...);
+ RunFromMainLoop(closure);
+}
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_MANAGER_INL_H_
diff --git a/update_manager/update_manager.cc b/update_manager/update_manager.cc
new file mode 100644
index 0000000..d3281fd
--- /dev/null
+++ b/update_manager/update_manager.cc
@@ -0,0 +1,20 @@
+// 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/update_manager/update_manager.h"
+
+#include "update_engine/update_manager/chromeos_policy.h"
+#include "update_engine/update_manager/state.h"
+
+namespace chromeos_update_manager {
+
+UpdateManager::UpdateManager(chromeos_update_engine::ClockInterface* clock,
+ State* state)
+ : state_(state), clock_(clock) {
+ // TODO(deymo): Make it possible to replace this policy with a different
+ // implementation with a build-time flag.
+ policy_.reset(new ChromeOSPolicy());
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/update_manager.conf.example b/update_manager/update_manager.conf.example
new file mode 100644
index 0000000..2d77974
--- /dev/null
+++ b/update_manager/update_manager.conf.example
@@ -0,0 +1,18 @@
+# Configuration file for the update-manager component of update_engine.
+#
+# Normally this file is loaded from /etc/update_manager.conf. If
+# running update_engine in developer mode (and only if running in
+# developer mode), we attempt to load
+#
+# /mnt/stateful_partition/etc/update_manager.conf
+#
+# and use it if it exists. If it doesn't exist, we fall back to
+# /etc/update_manager.conf.
+#
+# Note: changes to this file are not automatically applied. Use the
+# command "restart update-engine" from a root shell to make your
+# changes take effect.
+
+# Set to true if the device supports the concept of OOBE
+# (Out-Of-the-Box-Experience), false if it doesn't.
+is_oobe_enabled=true
diff --git a/update_manager/update_manager.h b/update_manager/update_manager.h
new file mode 100644
index 0000000..b3dfc7f
--- /dev/null
+++ b/update_manager/update_manager.h
@@ -0,0 +1,131 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_MANAGER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_MANAGER_H_
+
+#include <base/callback.h>
+#include <base/memory/ref_counted.h>
+#include <base/memory/scoped_ptr.h>
+
+#include "update_engine/clock_interface.h"
+#include "update_engine/update_manager/default_policy.h"
+#include "update_engine/update_manager/policy.h"
+#include "update_engine/update_manager/state.h"
+
+namespace chromeos_update_manager {
+
+// The main Update Manager singleton class.
+class UpdateManager {
+ public:
+ // Creates the UpdateManager instance, assuming ownership on the provided
+ // |state|.
+ UpdateManager(chromeos_update_engine::ClockInterface* clock,
+ State* state);
+
+ virtual ~UpdateManager() {}
+
+ // PolicyRequest() evaluates the given policy with the provided arguments and
+ // returns the result. The |policy_method| is the pointer-to-method of the
+ // Policy class for the policy request to call. The UpdateManager will call
+ // this method on the right policy. The pointer |result| must not be NULL and
+ // the remaining |args| depend on the arguments required by the passed
+ // |policy_method|.
+ //
+ // When the policy request succeeds, the |result| is set and the method
+ // returns EvalStatus::kSucceeded, otherwise, the |result| may not be set.
+ // Also, if the policy implementation should block, this method returns
+ // immediately with EvalStatus::kAskMeAgainLater. In case of failure
+ // EvalStatus::kFailed is returned and the |error| message is set, which must
+ // not be NULL.
+ //
+ // An example call to this method is:
+ // um.PolicyRequest(&Policy::SomePolicyMethod, &bool_result, arg1, arg2);
+ template<typename R, typename... ActualArgs, typename... ExpectedArgs>
+ EvalStatus PolicyRequest(
+ EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
+ std::string*, R*,
+ ExpectedArgs...) const,
+ R* result, ActualArgs...);
+
+ // Evaluates the given |policy_method| policy with the provided |args|
+ // arguments and calls the |callback| callback with the result when done.
+ //
+ // If the policy implementation should block, returning a
+ // EvalStatus::kAskMeAgainLater status the Update Manager will re-evaluate the
+ // policy until another status is returned. If the policy implementation based
+ // its return value solely on const variables, the callback will be called
+ // with the EvalStatus::kAskMeAgainLater status.
+ template<typename R, typename... ActualArgs, typename... ExpectedArgs>
+ void AsyncPolicyRequest(
+ base::Callback<void(EvalStatus, const R& result)> callback,
+ EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
+ std::string*, R*,
+ ExpectedArgs...) const,
+ ActualArgs... args);
+
+ protected:
+ // The UpdateManager receives ownership of the passed Policy instance.
+ void set_policy(const Policy* policy) {
+ policy_.reset(policy);
+ }
+
+ // State getter used for testing.
+ State* state() { return state_.get(); }
+
+ private:
+ FRIEND_TEST(UmUpdateManagerTest, PolicyRequestCallsPolicy);
+ FRIEND_TEST(UmUpdateManagerTest, PolicyRequestCallsDefaultOnError);
+ FRIEND_TEST(UmUpdateManagerTest, PolicyRequestDoesntBlock);
+ FRIEND_TEST(UmUpdateManagerTest, AsyncPolicyRequestDelaysEvaluation);
+
+ // EvaluatePolicy() evaluates the passed |policy_method| method on the current
+ // policy with the given |args| arguments. If the method fails, the default
+ // policy is used instead.
+ template<typename R, typename... Args>
+ EvalStatus EvaluatePolicy(
+ EvaluationContext* ec,
+ EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
+ std::string*, R*,
+ Args...) const,
+ R* result, Args... args);
+
+ // OnPolicyReadyToEvaluate() is called by the main loop when the evaluation
+ // of the given |policy_method| should be executed. If the evaluation finishes
+ // the |callback| callback is called passing the |result| and the |status|
+ // returned by the policy. If the evaluation returns an
+ // EvalStatus::kAskMeAgainLater state, the |callback| will NOT be called and
+ // the evaluation will be re-scheduled to be called later.
+ template<typename R, typename... Args>
+ void OnPolicyReadyToEvaluate(
+ scoped_refptr<EvaluationContext> ec,
+ base::Callback<void(EvalStatus status, const R& result)> callback,
+ EvalStatus (Policy::*policy_method)(EvaluationContext*, State*,
+ std::string*, R*,
+ Args...) const,
+ Args... args);
+
+ // The policy used by the UpdateManager. Note that since it is a const Policy,
+ // policy implementations are not allowed to persist state on this class.
+ scoped_ptr<const Policy> policy_;
+
+ // A safe default value to the current policy. This policy is used whenever
+ // a policy implementation fails with EvalStatus::kFailed.
+ const DefaultPolicy default_policy_;
+
+ // State Providers.
+ scoped_ptr<State> state_;
+
+ // Pointer to the mockable clock interface;
+ chromeos_update_engine::ClockInterface* clock_;
+
+ DISALLOW_COPY_AND_ASSIGN(UpdateManager);
+};
+
+} // namespace chromeos_update_manager
+
+// Include the implementation of the template methods.
+#include "update_engine/update_manager/update_manager-inl.h"
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_MANAGER_H_
diff --git a/update_manager/update_manager_unittest.cc b/update_manager/update_manager_unittest.cc
new file mode 100644
index 0000000..39427b3
--- /dev/null
+++ b/update_manager/update_manager_unittest.cc
@@ -0,0 +1,158 @@
+// 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 <algorithm>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/memory/scoped_ptr.h>
+#include <base/time/time.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/fake_clock.h"
+#include "update_engine/test_utils.h"
+#include "update_engine/update_manager/default_policy.h"
+#include "update_engine/update_manager/fake_state.h"
+#include "update_engine/update_manager/mock_policy.h"
+#include "update_engine/update_manager/umtest_utils.h"
+#include "update_engine/update_manager/update_manager.h"
+
+using base::Bind;
+using base::Callback;
+using base::Time;
+using base::TimeDelta;
+using chromeos_update_engine::FakeClock;
+using std::pair;
+using std::string;
+using std::vector;
+using testing::Return;
+using testing::StrictMock;
+using testing::_;
+
+namespace {
+
+// Generates a fixed timestamp for use in faking the current time.
+Time FixedTime() {
+ Time::Exploded now_exp;
+ now_exp.year = 2014;
+ now_exp.month = 3;
+ now_exp.day_of_week = 2;
+ now_exp.day_of_month = 18;
+ now_exp.hour = 8;
+ now_exp.minute = 5;
+ now_exp.second = 33;
+ now_exp.millisecond = 675;
+ return Time::FromLocalExploded(now_exp);
+}
+
+} // namespace
+
+namespace chromeos_update_manager {
+
+class UmUpdateManagerTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ fake_state_ = new FakeState();
+ umut_.reset(new UpdateManager(&fake_clock_, fake_state_));
+ }
+
+ FakeState* fake_state_; // Owned by the umut_.
+ FakeClock fake_clock_;
+ scoped_ptr<UpdateManager> umut_;
+};
+
+// The FailingPolicy implements a single method and make it always fail. This
+// class extends the DefaultPolicy class to allow extensions of the Policy
+// class without extending nor changing this test.
+class FailingPolicy : public DefaultPolicy {
+ virtual EvalStatus UpdateCheckAllowed(EvaluationContext* ec, State* state,
+ string* error,
+ UpdateCheckParams* result) const {
+ *error = "FailingPolicy failed.";
+ return EvalStatus::kFailed;
+ }
+};
+
+// The LazyPolicy always returns EvalStatus::kAskMeAgainLater.
+class LazyPolicy : public DefaultPolicy {
+ virtual EvalStatus UpdateCheckAllowed(EvaluationContext* ec, State* state,
+ string* error,
+ UpdateCheckParams* result) const {
+ return EvalStatus::kAskMeAgainLater;
+ }
+};
+
+// AccumulateCallsCallback() adds to the passed |acc| accumulator vector pairs
+// of EvalStatus and T instances. This allows to create a callback that keeps
+// track of when it is called and the arguments passed to it, to be used with
+// the UpdateManager::AsyncPolicyRequest().
+template<typename T>
+static void AccumulateCallsCallback(vector<pair<EvalStatus, T>>* acc,
+ EvalStatus status, const T& result) {
+ acc->push_back(std::make_pair(status, result));
+}
+
+// Tests that policy requests are completed successfully. It is important that
+// this tests cover all policy requests as defined in Policy.
+TEST_F(UmUpdateManagerTest, PolicyRequestCallUpdateCheckAllowed) {
+ UpdateCheckParams result;
+ EXPECT_EQ(EvalStatus::kSucceeded, umut_->PolicyRequest(
+ &Policy::UpdateCheckAllowed, &result));
+}
+
+TEST_F(UmUpdateManagerTest, PolicyRequestCallUpdateCanStart) {
+ const UpdateState update_state = {
+ FixedTime(), 1, TimeDelta::FromSeconds(15), TimeDelta::FromSeconds(60),
+ 4, 2, 8
+ };
+ UpdateCanStartResult result;
+ EXPECT_EQ(EvalStatus::kSucceeded,
+ umut_->PolicyRequest(&Policy::UpdateCanStart, &result, true,
+ update_state));
+}
+
+TEST_F(UmUpdateManagerTest, PolicyRequestCallsDefaultOnError) {
+ umut_->set_policy(new FailingPolicy());
+
+ // Tests that the DefaultPolicy instance is called when the method fails,
+ // which will set this as true.
+ UpdateCheckParams result;
+ result.updates_enabled = false;
+ EvalStatus status = umut_->PolicyRequest(
+ &Policy::UpdateCheckAllowed, &result);
+ EXPECT_EQ(EvalStatus::kSucceeded, status);
+ EXPECT_TRUE(result.updates_enabled);
+}
+
+TEST_F(UmUpdateManagerTest, PolicyRequestDoesntBlock) {
+ UpdateCheckParams result;
+ umut_->set_policy(new LazyPolicy());
+
+ EvalStatus status = umut_->PolicyRequest(
+ &Policy::UpdateCheckAllowed, &result);
+ EXPECT_EQ(EvalStatus::kAskMeAgainLater, status);
+}
+
+TEST_F(UmUpdateManagerTest, AsyncPolicyRequestDelaysEvaluation) {
+ // To avoid differences in code execution order between an AsyncPolicyRequest
+ // call on a policy that returns AskMeAgainLater the first time and one that
+ // succeeds the first time, we ensure that the passed callback is called from
+ // the main loop in both cases even when we could evaluate it right now.
+ umut_->set_policy(new FailingPolicy());
+
+ vector<pair<EvalStatus, UpdateCheckParams>> calls;
+ Callback<void(EvalStatus, const UpdateCheckParams& result)> callback =
+ Bind(AccumulateCallsCallback<UpdateCheckParams>, &calls);
+
+ umut_->AsyncPolicyRequest(callback, &Policy::UpdateCheckAllowed);
+ // The callback should wait until we run the main loop for it to be executed.
+ EXPECT_EQ(0, calls.size());
+ chromeos_update_engine::RunGMainLoopMaxIterations(100);
+ EXPECT_EQ(1, calls.size());
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/updater_provider.h b/update_manager/updater_provider.h
new file mode 100644
index 0000000..051ad3a
--- /dev/null
+++ b/update_manager/updater_provider.h
@@ -0,0 +1,91 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_UPDATER_PROVIDER_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_UPDATER_PROVIDER_H_
+
+#include <string>
+
+#include <base/time/time.h>
+
+#include "update_engine/update_manager/provider.h"
+#include "update_engine/update_manager/variable.h"
+
+namespace chromeos_update_manager {
+
+enum class Stage {
+ kIdle,
+ kCheckingForUpdate,
+ kUpdateAvailable,
+ kDownloading,
+ kVerifying,
+ kFinalizing,
+ kUpdatedNeedReboot,
+ kReportingErrorEvent,
+ kAttemptingRollback,
+};
+
+// Provider for Chrome OS update related information.
+class UpdaterProvider : public Provider {
+ public:
+ virtual ~UpdaterProvider() {}
+
+ // A variable returning the timestamp when the update engine was started in
+ // wallclock time.
+ virtual Variable<base::Time>* var_updater_started_time() = 0;
+
+ // A variable returning the last update check time.
+ virtual Variable<base::Time>* var_last_checked_time() = 0;
+
+ // A variable reporting the time when an update was last completed in the
+ // current boot cycle. Returns an error if an update completed time could not
+ // be read (e.g. no update was completed in the current boot cycle) or is
+ // invalid.
+ //
+ // IMPORTANT: The time reported is not the wallclock time reading at the time
+ // of the update, rather it is the point in time when the update completed
+ // relative to the current wallclock time reading. Therefore, the gap between
+ // the reported value and the current wallclock time is guaranteed to be
+ // monotonically increasing.
+ virtual Variable<base::Time>* var_update_completed_time() = 0;
+
+ // A variable returning the update progress (0.0 to 1.0).
+ virtual Variable<double>* var_progress() = 0;
+
+ // A variable returning the current update status.
+ virtual Variable<Stage>* var_stage() = 0;
+
+ // A variable returning the update target version.
+ virtual Variable<std::string>* var_new_version() = 0;
+
+ // A variable returning the update payload size. The payload size is
+ // guaranteed to be non-negative.
+ virtual Variable<int64_t>* var_payload_size() = 0;
+
+ // A variable returning the current channel.
+ virtual Variable<std::string>* var_curr_channel() = 0;
+
+ // A variable returning the update target channel.
+ virtual Variable<std::string>* var_new_channel() = 0;
+
+ // A variable indicating whether user settings allow P2P updates.
+ virtual Variable<bool>* var_p2p_enabled() = 0;
+
+ // A variable indicating whether user settings allow updates over a cellular
+ // network.
+ virtual Variable<bool>* var_cellular_enabled() = 0;
+
+ // A variable returning the number of consecutive failed update checks.
+ virtual Variable<unsigned int>* var_consecutive_failed_update_checks() = 0;
+
+ protected:
+ UpdaterProvider() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(UpdaterProvider);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_UPDATER_PROVIDER_H_
diff --git a/update_manager/variable.h b/update_manager/variable.h
new file mode 100644
index 0000000..3b77d0a
--- /dev/null
+++ b/update_manager/variable.h
@@ -0,0 +1,203 @@
+// 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.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_VARIABLE_H_
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_VARIABLE_H_
+
+#include <algorithm>
+#include <list>
+#include <string>
+
+#include <base/bind.h>
+#include <base/logging.h>
+#include <base/time/time.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "update_engine/update_manager/event_loop.h"
+
+namespace chromeos_update_manager {
+
+// The VariableMode specifies important behavior of the variable in terms of
+// whether, how and when the value of the variable changes.
+enum VariableMode {
+ // Const variables never changes during the life of a policy request, so the
+ // EvaluationContext caches the value even between different evaluations of
+ // the same policy request.
+ kVariableModeConst,
+
+ // Poll variables, or synchronous variables, represent a variable with a value
+ // that can be queried at any time, but it is not known when the value
+ // changes on the source of information. In order to detect if the value of
+ // the variable changes, it has to be queried again.
+ kVariableModePoll,
+
+ // Async variables are able to produce a signal or callback whenever the
+ // value changes. This means that it's not required to poll the value to
+ // detect when it changes, instead, you should register an observer to get
+ // a notification when that happens.
+ kVariableModeAsync,
+};
+
+// This class is a base class with the common functionality that doesn't
+// deppend on the variable's type, implemented by all the variables.
+class BaseVariable {
+ public:
+ // Interface for observing changes on variable value.
+ class ObserverInterface {
+ public:
+ virtual ~ObserverInterface() {}
+
+ // Called when the value on the variable changes.
+ virtual void ValueChanged(BaseVariable* variable) = 0;
+ };
+
+ virtual ~BaseVariable() {
+ if (!observer_list_.empty()) {
+ LOG(WARNING) << "Variable " << name_ << " deleted with "
+ << observer_list_.size() << " observers.";
+ }
+ DCHECK(observer_list_.empty()) << "Don't destroy the variable without "
+ "removing the observers.";
+ }
+
+ // Returns the variable name as a string.
+ const std::string& GetName() const {
+ return name_;
+ }
+
+ // Returns the variable mode.
+ VariableMode GetMode() const {
+ return mode_;
+ }
+
+ // For VariableModePoll variables, it returns the polling interval of this
+ // variable. In other case, it returns 0.
+ base::TimeDelta GetPollInterval() const {
+ return poll_interval_;
+ }
+
+ // Adds and removes observers for value changes on the variable. This only
+ // works for kVariableAsync variables since the other modes don't track value
+ // changes. Adding the same observer twice has no effect.
+ virtual void AddObserver(BaseVariable::ObserverInterface* observer) {
+ if (std::find(observer_list_.begin(), observer_list_.end(), observer) ==
+ observer_list_.end()) {
+ observer_list_.push_back(observer);
+ }
+ }
+
+ virtual void RemoveObserver(BaseVariable::ObserverInterface* observer) {
+ observer_list_.remove(observer);
+ }
+
+ protected:
+ // Creates a BaseVariable using the default polling interval (5 minutes).
+ BaseVariable(const std::string& name, VariableMode mode)
+ : BaseVariable(name, mode,
+ base::TimeDelta::FromMinutes(kDefaultPollMinutes)) {}
+
+ // Creates a BaseVariable with mode kVariableModePoll and the provided
+ // polling interval.
+ BaseVariable(const std::string& name, base::TimeDelta poll_interval)
+ : BaseVariable(name, kVariableModePoll, poll_interval) {}
+
+ // Calls ValueChanged on all the observers.
+ void NotifyValueChanged() {
+ // Fire all the observer methods from the main loop as single call. In order
+ // to avoid scheduling these callbacks when it is not needed, we check
+ // first the list of observers.
+ if (!observer_list_.empty()) {
+ RunFromMainLoop(base::Bind(&BaseVariable::OnValueChangedNotification,
+ base::Unretained(this)));
+ }
+ }
+
+ private:
+ friend class UmEvaluationContextTest;
+ FRIEND_TEST(UmBaseVariableTest, RepeatedObserverTest);
+ FRIEND_TEST(UmBaseVariableTest, NotifyValueChangedTest);
+ FRIEND_TEST(UmBaseVariableTest, NotifyValueRemovesObserversTest);
+
+ BaseVariable(const std::string& name, VariableMode mode,
+ base::TimeDelta poll_interval)
+ : name_(name), mode_(mode),
+ poll_interval_(mode == kVariableModePoll ?
+ poll_interval : base::TimeDelta()) {}
+
+ void OnValueChangedNotification() {
+ // A ValueChanged() method can change the list of observers, for example
+ // removing itself and invalidating the iterator, so we create a snapshot
+ // of the observers first. Also, to support the case when *another* observer
+ // is removed, we check for them.
+ std::list<BaseVariable::ObserverInterface*> observer_list_copy(
+ observer_list_);
+
+ for (auto& observer : observer_list_copy) {
+ if (std::find(observer_list_.begin(), observer_list_.end(), observer) !=
+ observer_list_.end()) {
+ observer->ValueChanged(this);
+ }
+ }
+ }
+
+ // The default PollInterval in minutes.
+ static constexpr int kDefaultPollMinutes = 5;
+
+ // The variable's name as a string.
+ const std::string name_;
+
+ // The variable's mode.
+ const VariableMode mode_;
+
+ // The variable's polling interval for VariableModePoll variable and 0 for
+ // other modes.
+ const base::TimeDelta poll_interval_;
+
+ // The list of value changes observers.
+ std::list<BaseVariable::ObserverInterface*> observer_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(BaseVariable);
+};
+
+// Interface to an Update Manager variable of a given type. Implementation
+// internals are hidden as protected members, since policies should not be
+// using them directly.
+template<typename T>
+class Variable : public BaseVariable {
+ public:
+ virtual ~Variable() {}
+
+ protected:
+ // Only allow to get values through the EvaluationContext class and not
+ // directly from the variable.
+ friend class EvaluationContext;
+
+ // Needed to be able to verify variable contents during unit testing.
+ friend class UmTestUtils;
+ FRIEND_TEST(UmRealRandomProviderTest, GetRandomValues);
+
+ Variable(const std::string& name, VariableMode mode)
+ : BaseVariable(name, mode) {}
+
+ Variable(const std::string& name, const base::TimeDelta poll_interval)
+ : BaseVariable(name, poll_interval) {}
+
+ // Gets the current value of the variable. The current value is copied to a
+ // new object and returned. The caller of this method owns the object and
+ // should delete it.
+ //
+ // In case of and error getting the current value or the |timeout| timeout is
+ // exceeded, a NULL value is returned and the |errmsg| is set.
+ //
+ // The caller can pass a NULL value for |errmsg|, in which case the error
+ // message won't be set.
+ virtual const T* GetValue(base::TimeDelta timeout, std::string* errmsg) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Variable);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_MANAGER_VARIABLE_H_
diff --git a/update_manager/variable_unittest.cc b/update_manager/variable_unittest.cc
new file mode 100644
index 0000000..09c7649
--- /dev/null
+++ b/update_manager/variable_unittest.cc
@@ -0,0 +1,158 @@
+// 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/update_manager/variable.h"
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/test_utils.h"
+
+using base::TimeDelta;
+using chromeos_update_engine::RunGMainLoopMaxIterations;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_manager {
+
+// Variable class that returns a value constructed with the default value.
+template <typename T>
+class DefaultVariable : public Variable<T> {
+ public:
+ DefaultVariable(const string& name, VariableMode mode)
+ : Variable<T>(name, mode) {}
+ DefaultVariable(const string& name, const TimeDelta& poll_interval)
+ : Variable<T>(name, poll_interval) {}
+ virtual ~DefaultVariable() {}
+
+ protected:
+ virtual const T* GetValue(TimeDelta /* timeout */,
+ string* /* errmsg */) {
+ return new T();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DefaultVariable);
+};
+
+TEST(UmBaseVariableTest, GetNameTest) {
+ DefaultVariable<int> var("var", kVariableModeConst);
+ EXPECT_EQ(var.GetName(), string("var"));
+}
+
+TEST(UmBaseVariableTest, GetModeTest) {
+ DefaultVariable<int> var("var", kVariableModeConst);
+ EXPECT_EQ(var.GetMode(), kVariableModeConst);
+ DefaultVariable<int> other_var("other_var", kVariableModePoll);
+ EXPECT_EQ(other_var.GetMode(), kVariableModePoll);
+}
+
+TEST(UmBaseVariableTest, DefaultPollIntervalTest) {
+ DefaultVariable<int> const_var("const_var", kVariableModeConst);
+ EXPECT_EQ(const_var.GetPollInterval(), TimeDelta());
+ DefaultVariable<int> poll_var("poll_var", kVariableModePoll);
+ EXPECT_EQ(poll_var.GetPollInterval(), TimeDelta::FromMinutes(5));
+}
+
+TEST(UmBaseVariableTest, GetPollIntervalTest) {
+ DefaultVariable<int> var("var", TimeDelta::FromMinutes(3));
+ EXPECT_EQ(var.GetMode(), kVariableModePoll);
+ EXPECT_EQ(var.GetPollInterval(), TimeDelta::FromMinutes(3));
+}
+
+class BaseVariableObserver : public BaseVariable::ObserverInterface {
+ public:
+ void ValueChanged(BaseVariable* variable) {
+ calls_.push_back(variable);
+ }
+
+ // List of called functions.
+ vector<BaseVariable*> calls_;
+};
+
+TEST(UmBaseVariableTest, RepeatedObserverTest) {
+ DefaultVariable<int> var("var", kVariableModeAsync);
+ BaseVariableObserver observer;
+ var.AddObserver(&observer);
+ EXPECT_EQ(var.observer_list_.size(), 1);
+ var.AddObserver(&observer);
+ EXPECT_EQ(var.observer_list_.size(), 1);
+ var.RemoveObserver(&observer);
+ EXPECT_EQ(var.observer_list_.size(), 0);
+ var.RemoveObserver(&observer);
+ EXPECT_EQ(var.observer_list_.size(), 0);
+}
+
+TEST(UmBaseVariableTest, NotifyValueChangedTest) {
+ DefaultVariable<int> var("var", kVariableModeAsync);
+ BaseVariableObserver observer1;
+ var.AddObserver(&observer1);
+ // Simulate a value change on the variable's implementation.
+ var.NotifyValueChanged();
+ ASSERT_EQ(0, observer1.calls_.size());
+ RunGMainLoopMaxIterations(100);
+
+ ASSERT_EQ(1, observer1.calls_.size());
+ // Check that the observer is called with the right argument.
+ EXPECT_EQ(&var, observer1.calls_[0]);
+
+ BaseVariableObserver observer2;
+ var.AddObserver(&observer2);
+ var.NotifyValueChanged();
+ RunGMainLoopMaxIterations(100);
+
+ // Check that all the observers are called.
+ EXPECT_EQ(2, observer1.calls_.size());
+ EXPECT_EQ(1, observer2.calls_.size());
+
+ var.RemoveObserver(&observer1);
+ var.RemoveObserver(&observer2);
+}
+
+class BaseVariableObserverRemover : public BaseVariable::ObserverInterface {
+ public:
+ BaseVariableObserverRemover() : calls_(0) {}
+
+ void ValueChanged(BaseVariable* variable) override {
+ for (auto& observer : remove_observers_) {
+ variable->RemoveObserver(observer);
+ }
+ calls_++;
+ }
+
+ void OnCallRemoveObserver(BaseVariable::ObserverInterface* observer) {
+ remove_observers_.push_back(observer);
+ }
+
+ int get_calls() { return calls_; }
+
+ private:
+ vector<BaseVariable::ObserverInterface*> remove_observers_;
+ int calls_;
+};
+
+// Tests that we can remove an observer from a Variable on the ValueChanged()
+// call to that observer.
+TEST(UmBaseVariableTest, NotifyValueRemovesObserversTest) {
+ DefaultVariable<int> var("var", kVariableModeAsync);
+ BaseVariableObserverRemover observer1;
+ BaseVariableObserverRemover observer2;
+
+ var.AddObserver(&observer1);
+ var.AddObserver(&observer2);
+
+ // Make each observer remove both observers on ValueChanged.
+ observer1.OnCallRemoveObserver(&observer1);
+ observer1.OnCallRemoveObserver(&observer2);
+ observer2.OnCallRemoveObserver(&observer1);
+ observer2.OnCallRemoveObserver(&observer2);
+
+ var.NotifyValueChanged();
+ RunGMainLoopMaxIterations(100);
+
+ EXPECT_EQ(1, observer1.get_calls() + observer2.get_calls());
+}
+
+} // namespace chromeos_update_manager