Revised GPIO module interface + GPIO discovery logic
* The GpioHandler class is no longer a static singleton, rather an
ordinary object with a dynamic guard against multiple instances. This
makes testing/mocking a lot easier and simplifies implementation.
* It uses a basic, mockable udev interface; the module comes with
complete unit testing of the discovery mechanism.
* Corresponding changes to user classes, including UpdateAttempter and
UpdateCheckScheduler.
Note that the implementation of the test mode signaling protocol is
currently a no-op, always returning false, and hence has no effect on
the update process yet. This mechanism will be implemented in a later
CL.
BUG=chromium-os:25397
TEST=Builds and passes unit tests (including new ones)
Change-Id: I2f6254db6799ff5ef8616314890833f6e3269ff6
Reviewed-on: https://gerrit.chromium.org/gerrit/22869
Reviewed-by: Gilad Arnold <garnold@chromium.org>
Tested-by: Gilad Arnold <garnold@chromium.org>
Commit-Ready: Gilad Arnold <garnold@chromium.org>
diff --git a/SConstruct b/SConstruct
index 0030bd5..a79a8a8 100644
--- a/SConstruct
+++ b/SConstruct
@@ -304,6 +304,8 @@
filesystem_iterator_unittest.cc
flimflam_proxy_unittest.cc
full_update_generator_unittest.cc
+ gpio_handler_unittest.cc
+ gpio_mock_udev_interface.cc
graph_utils_unittest.cc
http_fetcher_unittest.cc
metadata_unittest.cc
diff --git a/gpio_handler.cc b/gpio_handler.cc
index d1825bd..efd5e3d 100644
--- a/gpio_handler.cc
+++ b/gpio_handler.cc
@@ -4,333 +4,250 @@
#include "gpio_handler.h"
-#include <fcntl.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <base/eintr_wrapper.h>
+#include <base/memory/scoped_ptr.h>
#include <base/string_util.h>
-#include <glib.h>
+#include <base/stringprintf.h>
-#include "update_engine/utils.h"
+#include "update_engine/file_descriptor.h"
+using base::Time;
+using base::TimeDelta;
using std::string;
+using namespace chromeos_update_engine;
+
namespace chromeos_update_engine {
-const char* GpioHandler::dutflaga_dev_name_ = NULL;
-const char* GpioHandler::dutflagb_dev_name_ = NULL;
-
-namespace {
-// Names of udev properties that are linked to the GPIO chip device and identify
-// the two dutflag GPIOs on different boards.
-const char kIdGpioDutflaga[] = "ID_GPIO_DUTFLAGA";
-const char kIdGpioDutflagb[] = "ID_GPIO_DUTFLAGB";
-
-// Scoped closer for udev and udev_enumerate objects.
-// TODO(garnold) chromium-os:26934: it would be nice to generalize the different
-// ScopedFooCloser implementations in update engine using a single template.
-class ScopedUdevCloser {
- public:
- explicit ScopedUdevCloser(udev** udev_p) : udev_p_(udev_p) {}
- ~ScopedUdevCloser() {
- if (udev_p_ && *udev_p_) {
- udev_unref(*udev_p_);
- *udev_p_ = NULL;
- }
- }
- private:
- struct udev **udev_p_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedUdevCloser);
+const char* StandardGpioHandler::gpio_dirs_[kGpioDirMax] = {
+ "in", // kGpioDirIn
+ "out", // kGpioDirOut
};
-class ScopedUdevEnumerateCloser {
- public:
- explicit ScopedUdevEnumerateCloser(udev_enumerate **udev_enum_p) :
- udev_enum_p_(udev_enum_p) {}
- ~ScopedUdevEnumerateCloser() {
- if (udev_enum_p_ && *udev_enum_p_) {
- udev_enumerate_unref(*udev_enum_p_);
- *udev_enum_p_ = NULL;
- }
- }
- private:
- struct udev_enumerate** udev_enum_p_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedUdevEnumerateCloser);
+const char* StandardGpioHandler::gpio_vals_[kGpioValMax] = {
+ "1", // kGpioValUp
+ "0", // kGpioValDown
};
-} // namespace {}
-bool GpioHandler::IsGpioSignalingTest() {
- // Peek dut_flaga GPIO state.
- bool dutflaga_gpio_state;
- if (!GetDutflagGpioStatus(kDutflagaGpio, &dutflaga_gpio_state)) {
- LOG(WARNING) << "dutflaga GPIO reading failed, defaulting to non-test mode";
+const StandardGpioHandler::GpioDef
+StandardGpioHandler::gpio_defs_[kGpioIdMax] = {
+ { "dutflaga", "ID_GPIO_DUTFLAGA" }, // kGpioDutflaga
+ { "dutflagb", "ID_GPIO_DUTFLAGB" }, // kGpioDutflagb
+};
+
+unsigned StandardGpioHandler::num_instances_ = 0;
+
+
+StandardGpioHandler::StandardGpioHandler(UdevInterface* udev_iface,
+ bool is_defer_discovery)
+ : udev_iface_(udev_iface),
+ is_discovery_attempted_(false) {
+ CHECK(udev_iface);
+
+ // Ensure there's only one instance of this class.
+ CHECK_EQ(num_instances_, static_cast<unsigned>(0));
+ num_instances_++;
+
+ // If GPIO discovery not deferred, do it.
+ if (!(is_defer_discovery || DiscoverGpios())) {
+ LOG(WARNING) << "GPIO discovery failed";
+ }
+}
+
+StandardGpioHandler::~StandardGpioHandler() {
+ num_instances_--;
+}
+
+// TODO(garnold) currently, this function always returns false and avoids the
+// GPIO signaling protocol altogether; to be extended later.
+bool StandardGpioHandler::IsTestModeSignaled() {
+ // Attempt GPIO discovery.
+ if (!DiscoverGpios()) {
+ LOG(WARNING) << "GPIO discovery failed";
+ }
+
+ return false;
+}
+
+bool StandardGpioHandler::GpioChipUdevEnumHelper::SetupEnumFilters(
+ udev_enumerate* udev_enum) {
+ CHECK(udev_enum);
+
+ return !(gpio_handler_->udev_iface_->EnumerateAddMatchSubsystem(
+ udev_enum, "gpio") ||
+ gpio_handler_->udev_iface_->EnumerateAddMatchSysname(
+ udev_enum, "gpiochip*"));
+}
+
+bool StandardGpioHandler::GpioChipUdevEnumHelper::ProcessDev(udev_device* dev) {
+ CHECK(dev);
+
+ // Ensure we did not encounter more than one chip.
+ if (num_gpio_chips_++) {
+ LOG(ERROR) << "enumerated multiple GPIO chips";
return false;
}
- LOG(INFO) << "dutflaga GPIO reading: "
- << (dutflaga_gpio_state ? "on (non-test mode)" : "off (test mode)");
- return !dutflaga_gpio_state;
+ // Obtain GPIO descriptors.
+ for (int id = 0; id < kGpioIdMax; id++) {
+ const GpioDef* gpio_def = &gpio_defs_[id];
+ const char* descriptor =
+ gpio_handler_->udev_iface_->DeviceGetPropertyValue(
+ dev, gpio_def->udev_property);
+ if (!descriptor) {
+ LOG(ERROR) << "could not obtain " << gpio_def->name
+ << " descriptor using property " << gpio_def->udev_property;
+ return false;
+ }
+ gpio_handler_->gpios_[id].descriptor = descriptor;
+ }
+
+ return true;
}
-bool GpioHandler::GetDutflagGpioDevName(struct udev* udev,
- const string& gpio_dutflag_str,
- const char** dutflag_dev_name_p) {
- CHECK(udev && dutflag_dev_name_p);
+bool StandardGpioHandler::GpioChipUdevEnumHelper::Finalize() {
+ if (num_gpio_chips_ != 1) {
+ LOG(ERROR) << "could not enumerate a GPIO chip";
+ return false;
+ }
+ return true;
+}
- struct udev_enumerate* udev_enum = NULL;
- int num_gpio_dutflags = 0;
- const string gpio_dutflag_pattern = "*" + gpio_dutflag_str;
- int ret;
+bool StandardGpioHandler::GpioUdevEnumHelper::SetupEnumFilters(
+ udev_enumerate* udev_enum) {
+ CHECK(udev_enum);
+ const string gpio_pattern =
+ string("*").append(gpio_handler_->gpios_[id_].descriptor);
+ return !(
+ gpio_handler_->udev_iface_->EnumerateAddMatchSubsystem(
+ udev_enum, "gpio") ||
+ gpio_handler_->udev_iface_->EnumerateAddMatchSysname(
+ udev_enum, gpio_pattern.c_str()));
+}
- // Initialize udev enumerate context and closer.
- if (!(udev_enum = udev_enumerate_new(udev))) {
+bool StandardGpioHandler::GpioUdevEnumHelper::ProcessDev(udev_device* dev) {
+ CHECK(dev);
+
+ // Ensure we did not encounter more than one GPIO device.
+ if (num_gpios_++) {
+ LOG(ERROR) << "enumerated multiple GPIO devices for a given descriptor";
+ return false;
+ }
+
+ // Obtain GPIO device sysfs path.
+ const char* dev_path = gpio_handler_->udev_iface_->DeviceGetSyspath(dev);
+ if (!dev_path) {
+ LOG(ERROR) << "failed to obtain device syspath for GPIO "
+ << gpio_defs_[id_].name;
+ return false;
+ }
+ gpio_handler_->gpios_[id_].dev_path = dev_path;
+
+ LOG(INFO) << "obtained device syspath: " << gpio_defs_[id_].name << " -> "
+ << gpio_handler_->gpios_[id_].dev_path;
+ return true;
+}
+
+bool StandardGpioHandler::GpioUdevEnumHelper::Finalize() {
+ if (num_gpios_ != 1) {
+ LOG(ERROR) << "could not enumerate GPIO device " << gpio_defs_[id_].name;
+ return false;
+ }
+ return true;
+}
+
+
+bool StandardGpioHandler::InitUdevEnum(struct udev* udev,
+ UdevEnumHelper* enum_helper) {
+ // Obtain a udev enumerate object.
+ struct udev_enumerate* udev_enum;
+ if (!(udev_enum = udev_iface_->EnumerateNew(udev))) {
LOG(ERROR) << "failed to obtain udev enumerate context";
return false;
}
- ScopedUdevEnumerateCloser udev_enum_closer(&udev_enum);
- // Populate filters for find an initialized GPIO chip.
- if ((ret = udev_enumerate_add_match_subsystem(udev_enum, "gpio")) ||
- (ret = udev_enumerate_add_match_sysname(udev_enum,
- gpio_dutflag_pattern.c_str()))) {
- LOG(ERROR) << "failed to initialize udev enumerate context (" << ret << ")";
+ // Assign enumerate object to closer.
+ scoped_ptr<UdevInterface::UdevEnumerateCloser>
+ udev_enum_closer(udev_iface_->NewUdevEnumerateCloser(&udev_enum));
+
+ // Setup enumeration filters.
+ if (!enum_helper->SetupEnumFilters(udev_enum)) {
+ LOG(ERROR) << "failed to setup udev enumerate filters";
return false;
}
- // Obtain list of matching devices.
- if ((ret = udev_enumerate_scan_devices(udev_enum))) {
- LOG(ERROR) << "udev enumerate context scan failed (error code "
- << ret << ")";
+ // Scan for matching devices.
+ if (udev_iface_->EnumerateScanDevices(udev_enum)) {
+ LOG(ERROR) << "udev enumerate scan failed";
return false;
}
- // Iterate over matching devices, obtain GPIO dut_flaga identifier.
+ // Iterate over matching devices.
struct udev_list_entry* list_entry;
- udev_list_entry_foreach(list_entry,
- udev_enumerate_get_list_entry(udev_enum)) {
- // Make sure we're not enumerating more than one device.
- num_gpio_dutflags++;
- if (num_gpio_dutflags > 1) {
- LOG(WARNING) <<
- "enumerated multiple dutflag GPIOs, ignoring this one";
- continue;
- }
-
+ for (list_entry = udev_iface_->EnumerateGetListEntry(udev_enum);
+ list_entry; list_entry = udev_iface_->ListEntryGetNext(list_entry)) {
// Obtain device name.
- const char* dev_name = udev_list_entry_get_name(list_entry);
- if (!dev_name) {
- LOG(WARNING) << "enumerated device has a null name string, skipping";
- continue;
+ const char* dev_path = udev_iface_->ListEntryGetName(list_entry);
+ if (!dev_path) {
+ LOG(ERROR) << "enumerated device has a null name string";
+ return false;
}
// Obtain device object.
- struct udev_device* dev = udev_device_new_from_syspath(udev, dev_name);
+ struct udev_device* dev = udev_iface_->DeviceNewFromSyspath(udev, dev_path);
if (!dev) {
- LOG(WARNING) <<
- "obtained a null device object for enumerated device, skipping";
- continue;
- }
-
- // Obtain device syspath.
- const char* dev_syspath = udev_device_get_syspath(dev);
- if (dev_syspath) {
- LOG(INFO) << "obtained device syspath: " << dev_syspath;
- *dutflag_dev_name_p = strdup(dev_syspath);
- } else {
- LOG(WARNING) << "could not obtain device syspath";
- }
-
- udev_device_unref(dev);
- }
-
- return true;
-}
-
-bool GpioHandler::GetDutflagGpioDevNames(string* dutflaga_dev_name_p,
- string* dutflagb_dev_name_p) {
- if (!(dutflaga_dev_name_p || dutflagb_dev_name_p))
- return true; // No output pointers, nothing to do.
-
- string gpio_dutflaga_str, gpio_dutflagb_str;
-
- if (!(dutflaga_dev_name_ && dutflagb_dev_name_)) {
- struct udev* udev = NULL;
- struct udev_enumerate* udev_enum = NULL;
- int num_gpio_chips = 0;
- const char* id_gpio_dutflaga = NULL;
- const char* id_gpio_dutflagb = NULL;
- int ret;
-
- LOG(INFO) << "begin discovery of dut_flaga/b devices";
-
- // Obtain libudev instance and closer.
- if (!(udev = udev_new())) {
- LOG(ERROR) << "failed to obtain libudev instance";
+ LOG(ERROR) << "obtained a null device object for enumerated device";
return false;
}
- ScopedUdevCloser udev_closer(&udev);
+ scoped_ptr<UdevInterface::UdevDeviceCloser>
+ dev_closer(udev_iface_->NewUdevDeviceCloser(&dev));
- // Initialize a udev enumerate object and closer with a bounded lifespan.
- {
- if (!(udev_enum = udev_enumerate_new(udev))) {
- LOG(ERROR) << "failed to obtain udev enumerate context";
- return false;
- }
- ScopedUdevEnumerateCloser udev_enum_closer(&udev_enum);
-
- // Populate filters for find an initialized GPIO chip.
- if ((ret = udev_enumerate_add_match_subsystem(udev_enum, "gpio")) ||
- (ret = udev_enumerate_add_match_sysname(udev_enum, "gpiochip*")) ||
- (ret = udev_enumerate_add_match_property(udev_enum,
- kIdGpioDutflaga, "*")) ||
- (ret = udev_enumerate_add_match_property(udev_enum,
- kIdGpioDutflagb, "*"))) {
- LOG(ERROR) << "failed to initialize udev enumerate context ("
- << ret << ")";
- return false;
- }
-
- // Obtain list of matching devices.
- if ((ret = udev_enumerate_scan_devices(udev_enum))) {
- LOG(ERROR) << "udev enumerate context scan failed (" << ret << ")";
- return false;
- }
-
- // Iterate over matching devices, obtain GPIO dut_flaga identifier.
- struct udev_list_entry* list_entry;
- udev_list_entry_foreach(list_entry,
- udev_enumerate_get_list_entry(udev_enum)) {
- // Make sure we're not enumerating more than one device.
- num_gpio_chips++;
- if (num_gpio_chips > 1) {
- LOG(WARNING) << "enumerated multiple GPIO chips, ignoring this one";
- continue;
- }
-
- // Obtain device name.
- const char* dev_name = udev_list_entry_get_name(list_entry);
- if (!dev_name) {
- LOG(WARNING) << "enumerated device has a null name string, skipping";
- continue;
- }
-
- // Obtain device object.
- struct udev_device* dev = udev_device_new_from_syspath(udev, dev_name);
- if (!dev) {
- LOG(WARNING) <<
- "obtained a null device object for enumerated device, skipping";
- continue;
- }
-
- // Obtain dut_flaga/b identifiers.
- id_gpio_dutflaga =
- udev_device_get_property_value(dev, kIdGpioDutflaga);
- id_gpio_dutflagb =
- udev_device_get_property_value(dev, kIdGpioDutflagb);
- if (id_gpio_dutflaga && id_gpio_dutflagb) {
- LOG(INFO) << "found dut_flaga/b identifiers: a=" << id_gpio_dutflaga
- << " b=" << id_gpio_dutflagb;
-
- gpio_dutflaga_str = id_gpio_dutflaga;
- gpio_dutflagb_str = id_gpio_dutflagb;
- } else {
- LOG(ERROR) << "GPIO chip missing dut_flaga/b properties";
- }
-
- udev_device_unref(dev);
- }
- }
-
- // Obtain dut_flaga, reusing the same udev instance.
- if (!dutflaga_dev_name_ && !gpio_dutflaga_str.empty()) {
- LOG(INFO) << "discovering device for GPIO dut_flaga ";
- if (!GetDutflagGpioDevName(udev, gpio_dutflaga_str,
- &dutflaga_dev_name_)) {
- LOG(ERROR) << "discovery of dut_flaga GPIO device failed";
- return false;
- }
- }
-
- // Now obtain dut_flagb.
- if (!dutflagb_dev_name_ && !gpio_dutflagb_str.empty()) {
- LOG(INFO) << "discovering device for GPIO dut_flagb";
- if (!GetDutflagGpioDevName(udev, gpio_dutflagb_str,
- &dutflagb_dev_name_)) {
- LOG(ERROR) << "discovery of dut_flagb GPIO device failed";
- return false;
- }
- }
-
- LOG(INFO) << "end discovery of dut_flaga/b devices";
+ if (!enum_helper->ProcessDev(dev))
+ return false;
}
- // Write cached GPIO dutflag(s) to output strings.
- if (dutflaga_dev_name_p && dutflaga_dev_name_)
- *dutflaga_dev_name_p = dutflaga_dev_name_;
- if (dutflagb_dev_name_p && dutflagb_dev_name_)
- *dutflagb_dev_name_p = dutflagb_dev_name_;
+ // Make sure postconditions were met.
+ return enum_helper->Finalize();
+}
+
+bool StandardGpioHandler::DiscoverGpios() {
+ if (is_discovery_attempted_)
+ return true;
+
+ is_discovery_attempted_ = true;
+
+ // Obtain libudev instance and attach to a dedicated closer.
+ struct udev* udev;
+ if (!(udev = udev_iface_->New())) {
+ LOG(ERROR) << "failed to obtain libudev instance";
+ return false;
+ }
+ scoped_ptr<UdevInterface::UdevCloser>
+ udev_closer(udev_iface_->NewUdevCloser(&udev));
+
+ // Enumerate GPIO chips, scanning for GPIO descriptors.
+ GpioChipUdevEnumHelper chip_enum_helper(this);
+ if (!InitUdevEnum(udev, &chip_enum_helper)) {
+ LOG(ERROR) << "enumeration error, aborting GPIO discovery";
+ return false;
+ }
+
+ // Obtain device names for all discovered GPIOs, reusing the udev instance.
+ for (int id = 0; id < kGpioIdMax; id++) {
+ GpioUdevEnumHelper gpio_enum_helper(this, static_cast<GpioId>(id));
+ if (!InitUdevEnum(udev, &gpio_enum_helper)) {
+ LOG(ERROR) << "enumeration error, aborting GPIO discovery";
+ return false;
+ }
+ }
return true;
}
-bool GpioHandler::GetDutflagGpioStatus(GpioHandler::DutflagGpioId id,
- bool* status_p) {
- CHECK(status_p);
+bool StandardGpioHandler::GetGpioDevName(StandardGpioHandler::GpioId id,
+ string* dev_path_p) {
+ CHECK(id >= 0 && id < kGpioIdMax && dev_path_p);
- // Obtain GPIO device file name.
- string dutflag_dev_name;
- switch (id) {
- case kDutflagaGpio:
- GetDutflagGpioDevNames(&dutflag_dev_name, NULL);
- break;
- case kDutflagbGpio:
- GetDutflagGpioDevNames(NULL, &dutflag_dev_name);
- break;
- default:
- LOG(FATAL) << "invalid dutflag GPIO id: " << id;
- }
-
- if (dutflag_dev_name.empty()) {
- LOG(WARNING) << "could not find dutflag GPIO device";
- return false;
- }
-
- // Open device for reading.
- string dutflaga_value_dev_name = dutflag_dev_name + "/value";
- int dutflaga_fd = HANDLE_EINTR(open(dutflaga_value_dev_name.c_str(), 0));
- if (dutflaga_fd < 0) {
- PLOG(ERROR) << "opening dutflaga GPIO device file failed";
- return false;
- }
- ScopedEintrSafeFdCloser dutflaga_fd_closer(&dutflaga_fd);
-
- // Read the dut_flaga GPIO signal. We attempt to read more than---but expect
- // to receive exactly---two characters: a '0' or '1', and a newline. This is
- // to ensure that the GPIO device returns a legible result.
- char buf[3];
- int ret = HANDLE_EINTR(read(dutflaga_fd, buf, 3));
- if (ret != 2) {
- if (ret < 0)
- PLOG(ERROR) << "reading dutflaga GPIO status failed";
- else
- LOG(ERROR) << "read more than one byte (" << ret << ")";
- return false;
- }
-
- // Identify and write GPIO status.
- char c = buf[0];
- if ((c == '0' || c == '1') && buf[1] == '\n') {
- *status_p = (c == '1');
- } else {
- buf[2] = '\0';
- LOG(ERROR) << "read unexpected value from dutflaga GPIO: " << buf;
- return false;
- }
-
+ *dev_path_p = gpios_[id].dev_path;
return true;
}
diff --git a/gpio_handler.h b/gpio_handler.h
index c3d5a38..b269266 100644
--- a/gpio_handler.h
+++ b/gpio_handler.h
@@ -9,61 +9,192 @@
#include <string>
-#include <base/basictypes.h>
+#include <base/time.h>
+
+#include "update_engine/file_descriptor.h"
+#include "update_engine/udev_interface.h"
namespace chromeos_update_engine {
-// Handles GPIO signals, which are used for indicating a lab test environment.
-// This class detects, reads and decides whether a test scenario is in effect,
-// which can be used by client code to trigger test-specific behavior. This
-// class has only static members/methods and cannot be instantiated.
+// An abstract GPIO handler interface. Serves as basic for both concrete and
+// mock implementations.
class GpioHandler {
public:
- // Returns true iff GPIOs have been signaled to indicate an automated test
- // case. This triggers a discovery and reading of the dutflaga/b GPIOs.
- static bool IsGpioSignalingTest();
+ GpioHandler() {}
+ virtual ~GpioHandler() {}; // ensure virtual destruction
+
+ // Returns true iff GPIOs have been used to signal an automated test case.
+ // This call may trigger a (deferred) GPIO discovery step prior to engaging in
+ // the signaling protocol; if discovery did not reveal GPIO devices, or the
+ // protocol has terminated prematurely, it will conservatively default to
+ // false.
+ virtual bool IsTestModeSignaled() = 0;
private:
- // This class cannot be instantiated.
- GpioHandler() {}
+ DISALLOW_COPY_AND_ASSIGN(GpioHandler);
+};
- // Enumerator for dutflag GPIOs.
- enum DutflagGpioId {
- kDutflagaGpio,
- kDutflagbGpio,
+
+// Concrete implementation of GPIO signal handling. Currently, it only utilizes
+// the two Chromebook-specific GPIOs (aka 'dut_flaga' and 'dut_flagb') in
+// deciding whether a lab test mode has been signaled. Internal logic includes
+// detection, setup and reading from / writing to GPIOs. Detection is done via
+// libudev calls. This class should be instantiated at most once to avoid race
+// conditions in communicating over GPIO signals; instantiating a second
+// object will actually cause a runtime error.
+class StandardGpioHandler : public GpioHandler {
+ public:
+ // This constructor accepts a udev interface |udev_iface|. The value of
+ // |is_defer_discovery| determines whether GPIO discovery should be attempted
+ // right away (false) or done lazily, when necessitated by other calls (true).
+ StandardGpioHandler(UdevInterface* udev_iface, bool is_defer_discovery);
+
+ // Free all resources, allow to reinstantiate.
+ virtual ~StandardGpioHandler();
+
+ // Returns true iff GPIOs have been used to signal an automated test case. The
+ // check is performed at most once and the result is cached and returned on
+ // subsequent calls, unless |is_force| is true. This call may trigger a
+ // delayed GPIO discovery prior to engaging in the signaling protocol; if the
+ // delay period has not elapsed, it will conservatively default to false.
+ virtual bool IsTestModeSignaled();
+
+ private:
+ // GPIO identifiers, currently includes only the two dutflags.
+ enum GpioId {
+ kGpioIdDutflaga = 0,
+ kGpioIdDutflagb,
+ kGpioIdMax // marker, do not remove!
};
- // Gets the fully qualified sysfs name of a dutflag device. |udev| is a live
- // libudev instance; |gpio_dutflag_str| is the identifier for the requested
- // dutflag GPIO. The output is stored in the string pointed to by
- // |dutflag_dev_name_p|. Returns true upon success, false otherwise.
- static bool GetDutflagGpioDevName(struct udev* udev,
- const std::string& gpio_dutflag_str,
- const char** dutflag_dev_name_p);
+ // GPIO direction specifier.
+ enum GpioDir {
+ kGpioDirIn = 0,
+ kGpioDirOut,
+ kGpioDirMax // marker, do not remove!
+ };
- // Gets the dut_flaga/b GPIO device names and copies them into the two string
- // arguments, respectively. A string pointer may be null, in which case
- // discovery will not be attempted for the corresponding device. The function
- // caches these strings, which are assumed to be hardware constants. Returns
+ // GPIO value.
+ enum GpioVal {
+ kGpioValUp = 0,
+ kGpioValDown,
+ kGpioValMax // marker, do not remove!
+ };
+
+ // GPIO definition data.
+ struct GpioDef {
+ const char* name; // referential name of this GPIO
+ const char* udev_property; // udev property containing the device id
+ };
+
+ // GPIO runtime control structure.
+ struct Gpio {
+ std::string descriptor; // unique GPIO descriptor
+ std::string dev_path; // sysfs device name
+ };
+
+ // Various constants.
+ static const int kServoInputResponseTimeoutInSecs = 3;
+ static const int kServoInputNumChecksPerSec = 5;
+ static const int kServoOutputResponseWaitInSecs = 2;
+
+ // GPIO value/direction conversion tables.
+ static const char* gpio_dirs_[kGpioDirMax];
+ static const char* gpio_vals_[kGpioValMax];
+
+ // GPIO definitions.
+ static const GpioDef gpio_defs_[kGpioIdMax];
+
+ // Udev device enumeration helper classes. First here is an interface
+ // definition, which provides callbacks for enumeration setup and processing.
+ class UdevEnumHelper {
+ public:
+ UdevEnumHelper(StandardGpioHandler* gpio_handler)
+ : gpio_handler_(gpio_handler) {}
+
+ // Setup the enumeration filters.
+ virtual bool SetupEnumFilters(udev_enumerate* udev_enum) = 0;
+
+ // Processes an enumerated device. Returns true upon success, false
+ // otherwise.
+ virtual bool ProcessDev(udev_device* dev) = 0;
+
+ // Finalize the enumeration.
+ virtual bool Finalize() = 0;
+
+ protected:
+ StandardGpioHandler* gpio_handler_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(UdevEnumHelper);
+ };
+
+ // Specialized udev enumerate helper for extracting GPIO descriptors from the
+ // GPIO chip device.
+ class GpioChipUdevEnumHelper : public UdevEnumHelper {
+ public:
+ GpioChipUdevEnumHelper(StandardGpioHandler* gpio_handler)
+ : UdevEnumHelper(gpio_handler), num_gpio_chips_(0) {}
+ virtual bool SetupEnumFilters(udev_enumerate* udev_enum);
+ virtual bool ProcessDev(udev_device* dev);
+ virtual bool Finalize();
+
+ private:
+ // Records the number of times a GPIO chip has been enumerated (should not
+ // exceed 1).
+ int num_gpio_chips_;
+
+ DISALLOW_COPY_AND_ASSIGN(GpioChipUdevEnumHelper);
+ };
+
+ // Specialized udev enumerate helper for extracting a sysfs device path from a
+ // GPIO device.
+ class GpioUdevEnumHelper : public UdevEnumHelper {
+ public:
+ GpioUdevEnumHelper(StandardGpioHandler* gpio_handler, GpioId id)
+ : UdevEnumHelper(gpio_handler), num_gpios_(0), id_(id) {}
+ virtual bool SetupEnumFilters(udev_enumerate* udev_enum);
+ virtual bool ProcessDev(udev_device* dev);
+ virtual bool Finalize();
+
+ private:
+ // Records the number of times a GPIO has been enumerated with a given
+ // descriptor (should not exceed 1).
+ int num_gpios_;
+
+ // The enumerated GPIO identifier.
+ GpioId id_;
+
+ DISALLOW_COPY_AND_ASSIGN(GpioUdevEnumHelper);
+ };
+
+ // Attempt GPIO discovery, at most once. Returns true if discovery process was
+ // successfully completed or already attempted, false otherwise.
+ bool DiscoverGpios();
+
+ // An initialization helper performing udev enumeration. |enum_helper|
+ // implements an enumeration initialization and processing methods. Returns
// true upon success, false otherwise.
- static bool GetDutflagGpioDevNames(std::string* dutflaga_dev_name_p,
- std::string* dutflagb_dev_name_p);
+ bool InitUdevEnum(struct udev* udev, UdevEnumHelper* enum_helper);
- // Writes the dut_flaga GPIO status into its argument, where true/false stand
- // for "on"/"off", respectively. Returns true upon success, false otherwise
- // (in which case no value is written to |status|).
- static bool GetDutflagaGpio(bool* status);
+ // Assigns a copy of the device name of GPIO |id| to |dev_path_p|. Assumes
+ // initialization. Returns true upon success, false otherwise.
+ bool GetGpioDevName(GpioId id, std::string* dev_path_p);
- // Reads the value of a dut_flag GPIO |id| and stores it in |status_p|.
- // Returns true upon success, false otherwise (which also means that the GPIO
- // value was not stored and should not be used).
- static bool GetDutflagGpioStatus(DutflagGpioId id, bool* status_p);
+ // Dynamic counter for the number of instances this class has. Used to enforce
+ // that no more than one instance created. Thread-unsafe.
+ static unsigned num_instances_;
- // Dutflaga/b GPIO device names.
- static const char* dutflaga_dev_name_;
- static const char* dutflagb_dev_name_;
+ // GPIO control structures.
+ Gpio gpios_[kGpioIdMax];
- DISALLOW_COPY_AND_ASSIGN(GpioHandler);
+ // Udev interface.
+ UdevInterface* const udev_iface_;
+
+ // Indicates whether GPIO discovery was performed.
+ bool is_discovery_attempted_;
+
+ DISALLOW_COPY_AND_ASSIGN(StandardGpioHandler);
};
} // namespace chromeos_update_engine
diff --git a/gpio_handler_unittest.cc b/gpio_handler_unittest.cc
new file mode 100644
index 0000000..a133ac7
--- /dev/null
+++ b/gpio_handler_unittest.cc
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 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 "update_engine/gpio_handler.h"
+#include "update_engine/gpio_mock_udev_interface.h"
+
+// Some common strings used by the different cooperating mocks for this module.
+// We use preprocessor constants to allow concatenation at compile-time.
+#define MOCK_GPIO_CHIP_ID "100"
+#define MOCK_DUTFLAGA_GPIO_ID "101"
+#define MOCK_DUTFLAGB_GPIO_ID "102"
+#define MOCK_SYSFS_PREFIX "/mock/sys/class/gpio"
+
+namespace chromeos_update_engine {
+
+class StandardGpioHandlerTest : public ::testing::Test {};
+
+TEST(StandardGpioHandlerTest, NormalInitTest) {
+ // Ensure that initialization of the GPIO module works as expected, and that
+ // all udev resources are deallocated afterwards. The mock file descriptor is
+ // not to be used.
+ StandardGpioMockUdevInterface mock_udev;
+ StandardGpioHandler gpio_hander(&mock_udev, false);
+ mock_udev.ExpectAllResourcesDeallocated();
+ mock_udev.ExpectDiscoverySuccess();
+}
+
+TEST(StandardGpioHandlerTest, MultiGpioChipInitTest) {
+ // Attempt GPIO discovery with a udev mock that returns two GPIO chip devices.
+ // It should fail, of course. The mock file descriptor is not to be used.
+ MultiChipGpioMockUdevInterface mock_udev;
+ StandardGpioHandler gpio_handler(&mock_udev, false);
+ mock_udev.ExpectAllResourcesDeallocated();
+ mock_udev.ExpectDiscoveryFail();
+}
+
+TEST(StandardGpioHandlerTest, NormalModeGpioSignalingTest) {
+ // Initialize the GPIO module, run the signaling procedure, ensure that it
+ // concluded that this is a normal mode run.
+ StandardGpioMockUdevInterface mock_udev;
+ StandardGpioHandler gpio_handler(&mock_udev, false);
+ EXPECT_FALSE(gpio_handler.IsTestModeSignaled());
+ mock_udev.ExpectAllResourcesDeallocated();
+}
+
+TEST(StandardGpioHandlerTest, DeferredInitNormalModeGpioSignalingTest) {
+ // Initialize the GPIO module with deferred discovery, run the signaling
+ // procedure, ensure that it concluded that this is a normal mode run.
+ StandardGpioMockUdevInterface mock_udev;
+ StandardGpioHandler gpio_handler(&mock_udev, true);
+ EXPECT_FALSE(gpio_handler.IsTestModeSignaled());
+ mock_udev.ExpectAllResourcesDeallocated();
+}
+
+} // namespace chromeos_update_engine
diff --git a/gpio_handler_unittest.h b/gpio_handler_unittest.h
new file mode 100644
index 0000000..5780eaa
--- /dev/null
+++ b/gpio_handler_unittest.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2012 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_GPIO_HANDLER_UNITTEST_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_GPIO_HANDLER_UNITTEST_H__
+
+// This file contains various definitions that are shared by different mock
+// implementations that emulate GPIO behavior in the system.
+
+// Some common strings used by the different cooperating mocks for this module.
+// We use preprocessor constants to allow concatenation at compile-time.
+#define MOCK_GPIO_CHIP_ID "100"
+#define MOCK_DUTFLAGA_GPIO_ID "101"
+#define MOCK_DUTFLAGB_GPIO_ID "102"
+#define MOCK_SYSFS_PREFIX "/mock/sys/class/gpio"
+
+namespace chromeos_update_engine {
+
+// Mock GPIO identifiers, used by all mocks involved in unit testing the GPIO
+// module. These represent the GPIOs which the unit tests can cover. They should
+// generally match the GPIOs specified inside GpioHandler.
+enum MockGpioId {
+ kMockGpioIdDutflaga = 0,
+ kMockGpioIdDutflagb,
+ kMockGpioIdMax // marker, do not remove!
+};
+
+} // chromeos_update_engine
+
+#endif /* CHROMEOS_PLATFORM_UPDATE_ENGINE_GPIO_HANDLER_UNITTEST_H__ */
diff --git a/gpio_mock_udev_interface.cc b/gpio_mock_udev_interface.cc
new file mode 100644
index 0000000..22c7b7f
--- /dev/null
+++ b/gpio_mock_udev_interface.cc
@@ -0,0 +1,371 @@
+// Copyright (c) 2012 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/gpio_mock_udev_interface.h"
+
+#include <string>
+
+#include <base/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include "gpio_handler_unittest.h"
+
+namespace chromeos_update_engine {
+
+const char* StandardGpioMockUdevInterface::kUdevGpioSubsystem = "gpio";
+const char* StandardGpioMockUdevInterface::kUdevGpioChipSysname = "gpiochip*";
+
+const StandardGpioMockUdevInterface::GpioDescriptor
+ StandardGpioMockUdevInterface::gpio_descriptors_[kMockGpioIdMax] = {
+ { "ID_GPIO_DUTFLAGA", MOCK_DUTFLAGA_GPIO_ID },
+ { "ID_GPIO_DUTFLAGB", MOCK_DUTFLAGB_GPIO_ID },
+};
+
+const char* StandardGpioMockUdevInterface::enum_gpio_chip_dev_list_[] = {
+ "gpiochip" MOCK_GPIO_CHIP_ID,
+ NULL
+};
+const char* StandardGpioMockUdevInterface::enum_dutflaga_gpio_dev_list_[] = {
+ "gpio" MOCK_DUTFLAGA_GPIO_ID,
+ NULL
+};
+const char* StandardGpioMockUdevInterface::enum_dutflagb_gpio_dev_list_[] = {
+ "gpio" MOCK_DUTFLAGB_GPIO_ID,
+ NULL
+};
+
+StandardGpioMockUdevInterface::StandardGpioMockUdevInterface()
+ : udev_id_(0), udev_enum_id_(0), num_discovered_(0) {
+ std::fill_n(used_udev_ids_, MAX_UDEV_OBJECTS, false);
+ std::fill_n(used_udev_enum_ids_, MAX_UDEV_ENUM_OBJECTS, false);
+
+ gpio_chip_dev_ = (MockDevice) {
+ MOCK_SYSFS_PREFIX "/gpiochip" MOCK_GPIO_CHIP_ID,
+ gpio_descriptors_, kMockGpioIdMax, false, false
+ };
+ dutflaga_gpio_dev_ = (MockDevice) {
+ MOCK_SYSFS_PREFIX "/gpio" MOCK_DUTFLAGA_GPIO_ID, NULL, 0, true, false
+ };
+ dutflagb_gpio_dev_ = (MockDevice) {
+ MOCK_SYSFS_PREFIX "/gpio" MOCK_DUTFLAGB_GPIO_ID, NULL, 0, true, false
+ };
+}
+
+// Device lists are simply null-terminated arrays of strings.
+const char* StandardGpioMockUdevInterface::ListEntryGetName(
+ struct udev_list_entry* list_entry) {
+ const char** mock_list_entry = reinterpret_cast<const char**>(list_entry);
+ EXPECT_TRUE(mock_list_entry && *mock_list_entry);
+ if (!mock_list_entry)
+ return NULL;
+ // The mock list entry is the name string itself...
+ return *mock_list_entry;
+}
+
+struct udev_list_entry* StandardGpioMockUdevInterface::ListEntryGetNext(
+ struct udev_list_entry* list_entry) {
+ char** mock_list_entry = reinterpret_cast<char**>(list_entry);
+ EXPECT_TRUE(mock_list_entry && *mock_list_entry);
+ if (!mock_list_entry)
+ return NULL;
+ // Advance to the next element in the array.
+ mock_list_entry++;
+ return (*mock_list_entry ?
+ reinterpret_cast<struct udev_list_entry*>(mock_list_entry) : NULL);
+}
+
+struct udev_device* StandardGpioMockUdevInterface::DeviceNewFromSyspath(
+ struct udev* udev, const char* syspath) {
+ const size_t udev_id = UdevToId(udev);
+ if (udev_id >= MAX_UDEV_OBJECTS)
+ return NULL;
+ MockDevice* mock_dev;
+
+ // Generate the desired mock device based on the provided syspath.
+ if (!strcmp(syspath, enum_gpio_chip_dev_list_[0])) {
+ mock_dev = const_cast<MockDevice*>(&gpio_chip_dev_);
+ } else if (!strcmp(syspath, enum_dutflaga_gpio_dev_list_[0])) {
+ mock_dev = const_cast<MockDevice*>(&dutflaga_gpio_dev_);
+ } else if (!strcmp(syspath, enum_dutflagb_gpio_dev_list_[0])) {
+ mock_dev = const_cast<MockDevice*>(&dutflagb_gpio_dev_);
+ } else {
+ ADD_FAILURE();
+ return NULL;
+ }
+
+ EXPECT_FALSE(mock_dev->is_used);
+ if (mock_dev->is_used)
+ return NULL;
+ mock_dev->is_used = true;
+
+ return reinterpret_cast<struct udev_device*>(mock_dev);
+}
+
+const char* StandardGpioMockUdevInterface::DeviceGetPropertyValue(
+ struct udev_device* udev_device, const char* key) {
+ const MockDevice* mock_dev = UdevDeviceToMock(udev_device);
+ EXPECT_TRUE(mock_dev->properties);
+ if (!mock_dev->properties)
+ return NULL;
+ for (size_t i = 0; i < mock_dev->num_properties; i++)
+ if (!strcmp(key, mock_dev->properties[i].property))
+ return mock_dev->properties[i].value;
+ return NULL;
+}
+
+const char* StandardGpioMockUdevInterface::DeviceGetSyspath(
+ struct udev_device* udev_device) {
+ const MockDevice* mock_dev = UdevDeviceToMock(udev_device);
+ if (mock_dev->is_gpio)
+ num_discovered_++;
+ return mock_dev->syspath;
+}
+
+void StandardGpioMockUdevInterface::DeviceUnref(
+ struct udev_device* udev_device) {
+ MockDevice* mock_dev = UdevDeviceToMock(udev_device);
+ mock_dev->is_used = false;
+}
+
+struct udev_enumerate* StandardGpioMockUdevInterface::EnumerateNew(
+ struct udev* udev) {
+ const size_t udev_id = UdevToId(udev);
+ if (udev_id >= MAX_UDEV_OBJECTS)
+ return NULL;
+ // A new enumeration "pointer" is cast for an integer identifier.
+ EXPECT_LT(udev_enum_id_, MAX_UDEV_ENUM_OBJECTS);
+ if (udev_enum_id_ >= MAX_UDEV_ENUM_OBJECTS)
+ return NULL;
+ used_udev_enum_ids_[udev_enum_id_] = true;
+ return reinterpret_cast<struct udev_enumerate*>(++udev_enum_id_);
+}
+
+int StandardGpioMockUdevInterface::EnumerateAddMatchSubsystem(
+ struct udev_enumerate* udev_enum, const char* subsystem) {
+ const size_t udev_enum_id = UdevEnumToId(udev_enum);
+ if (udev_enum_id >= MAX_UDEV_ENUM_OBJECTS)
+ return -1;
+
+ // Ensure client is correctly requesting the GPIO subsystem.
+ EXPECT_STREQ(subsystem, kUdevGpioSubsystem);
+ if (strcmp(subsystem, kUdevGpioSubsystem))
+ return -1;
+
+ return 0;
+}
+
+int StandardGpioMockUdevInterface::EnumerateAddMatchSysname(
+ struct udev_enumerate* udev_enum, const char* sysname) {
+ const size_t udev_enum_id = UdevEnumToId(udev_enum);
+ if (udev_enum_id >= MAX_UDEV_ENUM_OBJECTS)
+ return -1;
+
+ // Ensure client is requesting the correct sysnamem, depending on the correct
+ // phase in the detection process.
+ switch (udev_enum_id) {
+ case 0: // looking for a GPIO chip
+ EXPECT_STREQ(sysname, kUdevGpioChipSysname);
+ if (strcmp(sysname, kUdevGpioChipSysname))
+ return -1;
+ break;
+ case 1: // looking for dut_flaga/b
+ case 2: {
+ const int gpio_id = udev_enum_id - 1;
+ const std::string gpio_pattern =
+ StringPrintf("*%s", gpio_descriptors_[gpio_id].value);
+ EXPECT_STREQ(sysname, gpio_pattern.c_str());
+ if (strcmp(sysname, gpio_pattern.c_str()))
+ return -1;
+ break;
+ }
+ default:
+ ADD_FAILURE(); // can't get here
+ return -1;
+ }
+
+ return 0;
+}
+
+int StandardGpioMockUdevInterface::EnumerateScanDevices(
+ struct udev_enumerate* udev_enum) {
+ const size_t udev_enum_id = UdevEnumToId(udev_enum);
+ if (udev_enum_id >= MAX_UDEV_ENUM_OBJECTS)
+ return -1;
+ return 0; // nothing to do, really
+}
+
+struct udev_list_entry* StandardGpioMockUdevInterface::EnumerateGetListEntry(
+ struct udev_enumerate* udev_enum) {
+ const size_t udev_enum_id = UdevEnumToId(udev_enum);
+ if (udev_enum_id >= MAX_UDEV_ENUM_OBJECTS)
+ return NULL;
+
+ // Return a list of devices corresponding to the provided enumeration.
+ switch (udev_enum_id) {
+ case 0:
+ return reinterpret_cast<struct udev_list_entry*>(
+ enum_gpio_chip_dev_list_);
+ case 1:
+ return reinterpret_cast<struct udev_list_entry*>(
+ enum_dutflaga_gpio_dev_list_);
+ case 2:
+ return reinterpret_cast<struct udev_list_entry*>(
+ enum_dutflagb_gpio_dev_list_);
+ }
+
+ ADD_FAILURE(); // can't get here
+ return NULL;
+}
+
+void StandardGpioMockUdevInterface::EnumerateUnref(
+ struct udev_enumerate* udev_enum) {
+ const size_t udev_enum_id = UdevEnumToId(udev_enum);
+ if (udev_enum_id >= MAX_UDEV_ENUM_OBJECTS)
+ return;
+ used_udev_enum_ids_[udev_enum_id] = false; // make sure it's freed just once
+}
+
+struct udev* StandardGpioMockUdevInterface::New() {
+ // The returned "pointer" is cast from an integer identifier.
+ EXPECT_LT(udev_id_, MAX_UDEV_OBJECTS);
+ if (udev_id_ >= MAX_UDEV_OBJECTS)
+ return NULL;
+ used_udev_ids_[udev_id_] = true;
+ return reinterpret_cast<struct udev*>(++udev_id_);
+}
+
+void StandardGpioMockUdevInterface::Unref(struct udev* udev) {
+ // Convert to object identifier, ensuring the object has been "allocated".
+ const size_t udev_id = UdevToId(udev);
+ if (udev_id >= MAX_UDEV_OBJECTS)
+ return;
+ used_udev_ids_[udev_id] = false; // make sure it's freed just once
+}
+
+void StandardGpioMockUdevInterface::ExpectAllResourcesDeallocated() const {
+ // Make sure that all handles were released.
+ for (size_t i = 0; i < MAX_UDEV_OBJECTS; i++)
+ EXPECT_FALSE(used_udev_ids_[i]);
+ for (size_t i = 0; i < MAX_UDEV_ENUM_OBJECTS; i++)
+ EXPECT_FALSE(used_udev_enum_ids_[i]);
+ EXPECT_FALSE(gpio_chip_dev_.is_used);
+ EXPECT_FALSE(dutflaga_gpio_dev_.is_used);
+ EXPECT_FALSE(dutflagb_gpio_dev_.is_used);
+}
+
+void StandardGpioMockUdevInterface::ExpectDiscoverySuccess() const {
+ EXPECT_EQ(num_discovered_, kMockGpioIdMax);
+}
+
+void StandardGpioMockUdevInterface::ExpectDiscoveryFail() const {
+ EXPECT_LT(num_discovered_, kMockGpioIdMax);
+}
+
+StandardGpioMockUdevInterface::MockDevice*
+StandardGpioMockUdevInterface::UdevDeviceToMock(
+ struct udev_device* udev_dev) const {
+ EXPECT_TRUE(udev_dev);
+ if (!udev_dev)
+ return NULL;
+ MockDevice* mock_dev = reinterpret_cast<MockDevice*>(udev_dev);
+ EXPECT_TRUE(mock_dev->is_used);
+ if (!mock_dev->is_used)
+ return NULL;
+ return mock_dev;
+}
+
+size_t StandardGpioMockUdevInterface::UdevEnumToId(
+ struct udev_enumerate* udev_enum) const {
+ EXPECT_TRUE(udev_enum);
+ size_t udev_enum_id = reinterpret_cast<size_t>(udev_enum) - 1;
+ EXPECT_LT(udev_enum_id, MAX_UDEV_ENUM_OBJECTS);
+ EXPECT_TRUE(used_udev_enum_ids_[udev_enum_id]);
+ if (!(udev_enum && udev_enum_id < MAX_UDEV_ENUM_OBJECTS &&
+ used_udev_enum_ids_[udev_enum_id]))
+ return MAX_UDEV_ENUM_OBJECTS;
+ return udev_enum_id;
+}
+
+size_t StandardGpioMockUdevInterface::UdevToId(struct udev* udev) const {
+ EXPECT_TRUE(udev);
+ size_t udev_id = reinterpret_cast<size_t>(udev) - 1;
+ EXPECT_LT(udev_id, MAX_UDEV_OBJECTS);
+ EXPECT_TRUE(used_udev_ids_[udev_id]);
+ if (!(udev && udev_id < MAX_UDEV_OBJECTS && used_udev_ids_[udev_id]))
+ return MAX_UDEV_OBJECTS;
+ return udev_id;
+}
+
+#define MOCK_GPIO_CHIP1_ID "200"
+#define MOCK_GPIO_CHIP2_ID "201"
+
+const char* MultiChipGpioMockUdevInterface::enum_gpio_chip_dev_list_[] = {
+ "gpiochip" MOCK_GPIO_CHIP1_ID,
+ "gpiochip" MOCK_GPIO_CHIP2_ID,
+ NULL
+};
+
+MultiChipGpioMockUdevInterface::MultiChipGpioMockUdevInterface() {
+ gpio_chip1_dev_ = (MockDevice) {
+ MOCK_SYSFS_PREFIX "/gpiochip" MOCK_GPIO_CHIP1_ID,
+ gpio_descriptors_, kMockGpioIdMax, false, false
+ };
+ gpio_chip2_dev_ = (MockDevice) {
+ MOCK_SYSFS_PREFIX "/gpiochip" MOCK_GPIO_CHIP2_ID,
+ gpio_descriptors_, kMockGpioIdMax, false, false
+ };
+}
+
+struct udev_device* MultiChipGpioMockUdevInterface::DeviceNewFromSyspath(
+ struct udev* udev, const char* syspath) {
+ const size_t udev_id = UdevToId(udev);
+ if (udev_id >= MAX_UDEV_OBJECTS)
+ return NULL;
+ MockDevice* mock_dev;
+
+ // Generate the desired mock device based on the provided syspath.
+ if (!strcmp(syspath, enum_gpio_chip_dev_list_[0])) {
+ mock_dev = const_cast<MockDevice*>(&gpio_chip1_dev_);
+ } else if (!strcmp(syspath, enum_gpio_chip_dev_list_[1])) {
+ mock_dev = const_cast<MockDevice*>(&gpio_chip2_dev_);
+ } else if (!strcmp(syspath, enum_dutflaga_gpio_dev_list_[0])) {
+ mock_dev = const_cast<MockDevice*>(&dutflaga_gpio_dev_);
+ } else if (!strcmp(syspath, enum_dutflagb_gpio_dev_list_[0])) {
+ mock_dev = const_cast<MockDevice*>(&dutflagb_gpio_dev_);
+ } else {
+ ADD_FAILURE();
+ return NULL;
+ }
+
+ EXPECT_FALSE(mock_dev->is_used);
+ if (mock_dev->is_used)
+ return NULL;
+ mock_dev->is_used = true;
+
+ return reinterpret_cast<struct udev_device*>(mock_dev);
+}
+
+struct udev_list_entry* MultiChipGpioMockUdevInterface::EnumerateGetListEntry(
+ struct udev_enumerate* udev_enum) {
+ const size_t udev_enum_id = UdevEnumToId(udev_enum);
+
+ // Return a list of devices corresponding to the provided enumeration.
+ switch (udev_enum_id) {
+ case 0:
+ return reinterpret_cast<struct udev_list_entry*>(
+ enum_gpio_chip_dev_list_);
+ case 1:
+ return reinterpret_cast<struct udev_list_entry*>(
+ enum_dutflaga_gpio_dev_list_);
+ case 2:
+ return reinterpret_cast<struct udev_list_entry*>(
+ enum_dutflagb_gpio_dev_list_);
+ }
+
+ ADD_FAILURE(); // can't get here
+ return NULL;
+}
+
+
+} // namespace chromeos_update_engine
diff --git a/gpio_mock_udev_interface.h b/gpio_mock_udev_interface.h
new file mode 100644
index 0000000..724a0f4
--- /dev/null
+++ b/gpio_mock_udev_interface.h
@@ -0,0 +1,134 @@
+// Copyright (c) 2012 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_GPIO_MOCK_UDEV_INTERFACE_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_GPIO_MOCK_UDEV_INTERFACE_H__
+
+#include "update_engine/gpio_handler.h"
+#include "update_engine/udev_interface.h"
+
+// A set of mock udev interfaces for unit testing of GPIO handler.
+
+// Constant defining number of allowable mock udev objects.
+#define MAX_UDEV_OBJECTS 1
+#define MAX_UDEV_ENUM_OBJECTS 3
+
+namespace chromeos_update_engine {
+
+// An abstract class providing some diagnostic methods for testing.
+class GpioMockUdevInterface : public UdevInterface {
+ public:
+ // Asserts that all resources have been properly deallocated.
+ virtual void ExpectAllResourcesDeallocated() const = 0;
+ // Asserts the the udev client has successfully discovered the syspath of the
+ // GPIO signals.
+ virtual void ExpectDiscoverySuccess() const = 0;
+ // Asserts the opposite.
+ virtual void ExpectDiscoveryFail() const = 0;
+};
+
+class StandardGpioMockUdevInterface : public GpioMockUdevInterface {
+ public:
+ // Default constructor.
+ StandardGpioMockUdevInterface();
+
+ // Inherited interface methods.
+ virtual const char* ListEntryGetName(struct udev_list_entry* list_entry);
+ virtual udev_list_entry* ListEntryGetNext(struct udev_list_entry* list_entry);
+
+ virtual struct udev_device* DeviceNewFromSyspath(struct udev* udev,
+ const char* syspath);
+ virtual const char* DeviceGetPropertyValue(struct udev_device* udev_device,
+ const char* key);
+ virtual const char* DeviceGetSyspath(struct udev_device* udev_device);
+ virtual void DeviceUnref(struct udev_device* udev_device);
+
+ virtual struct udev_enumerate* EnumerateNew(struct udev* udev);
+ virtual int EnumerateAddMatchSubsystem(struct udev_enumerate* udev_enum,
+ const char* subsystem);
+ virtual int EnumerateAddMatchSysname(struct udev_enumerate* udev_enum,
+ const char* sysname);
+ virtual int EnumerateScanDevices(struct udev_enumerate* udev_enum);
+ virtual struct udev_list_entry* EnumerateGetListEntry(
+ struct udev_enumerate* udev_enum);
+ virtual void EnumerateUnref(struct udev_enumerate* udev_enum);
+
+ virtual struct udev* New();
+ virtual void Unref(struct udev* udev);
+
+ virtual void ExpectAllResourcesDeallocated() const;
+ virtual void ExpectDiscoverySuccess() const;
+ virtual void ExpectDiscoveryFail() const;
+
+ protected:
+ // Some constants.
+ static const char* kUdevGpioSubsystem;
+ static const char* kUdevGpioChipSysname;
+ static const char* kMockGpioSysfsRoot;
+
+ // GPIO descriptor lookup.
+ struct GpioDescriptor {
+ const char* property;
+ const char* value;
+ };
+ static const GpioDescriptor gpio_descriptors_[];
+
+ // Numeric identifiers for new udev and enumerate objects.
+ size_t udev_id_;
+ size_t udev_enum_id_;
+
+ // Null-terminated lists of devices returned by various enumerations.
+ static const char* enum_gpio_chip_dev_list_[];
+ static const char* enum_dutflaga_gpio_dev_list_[];
+ static const char* enum_dutflagb_gpio_dev_list_[];
+
+ // Mock devices to be used during GPIO discovery. These contain the syspath
+ // and a set of properties that the device may contain.
+ struct MockDevice {
+ const char* syspath;
+ const GpioDescriptor* properties;
+ size_t num_properties;
+ bool is_gpio;
+ bool is_used;
+ };
+ MockDevice gpio_chip_dev_;
+ MockDevice dutflaga_gpio_dev_;
+ MockDevice dutflagb_gpio_dev_;
+
+ // Tracking active mock object handles.
+ bool used_udev_ids_[MAX_UDEV_OBJECTS];
+ bool used_udev_enum_ids_[MAX_UDEV_ENUM_OBJECTS];
+
+ // Track discovery progress of GPIO signals.
+ unsigned num_discovered_;
+
+ // Convert mock udev handles into internal handles, with sanity checks.
+ MockDevice* UdevDeviceToMock(struct udev_device* udev_dev) const;
+ size_t UdevEnumToId(struct udev_enumerate* udev_enum) const;
+ size_t UdevToId(struct udev* udev) const;
+};
+
+class MultiChipGpioMockUdevInterface : public StandardGpioMockUdevInterface {
+ public:
+ // Default constructor.
+ MultiChipGpioMockUdevInterface();
+
+ protected:
+ virtual struct udev_device* DeviceNewFromSyspath(struct udev* udev,
+ const char* syspath);
+ virtual struct udev_list_entry* EnumerateGetListEntry(
+ struct udev_enumerate* udev_enum);
+
+ // Null-terminated lists of devices returned by various enumerations.
+ static const char* enum_gpio_chip_dev_list_[];
+
+ // Mock devices to be used during GPIO discovery. These contain the syspath
+ // and a set of properties that the device may contain.
+ MockDevice gpio_chip1_dev_;
+ MockDevice gpio_chip2_dev_;
+};
+
+} // namespace chromeos_update_engine
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_GPIO_MOCK_UDEV_INTERFACE_H__
diff --git a/main.cc b/main.cc
index 790fe73..fc0319f 100644
--- a/main.cc
+++ b/main.cc
@@ -183,7 +183,8 @@
chromeos_update_engine::ConcreteDbusGlib dbus;
chromeos_update_engine::UpdateAttempter update_attempter(&prefs,
&metrics_lib,
- &dbus);
+ &dbus,
+ NULL);
// Create the dbus service object:
dbus_g_object_type_install_info(UPDATE_ENGINE_TYPE_SERVICE,
@@ -195,7 +196,8 @@
chromeos_update_engine::SetupDbusService(service);
// Schedule periodic update checks.
- chromeos_update_engine::UpdateCheckScheduler scheduler(&update_attempter);
+ chromeos_update_engine::UpdateCheckScheduler scheduler(&update_attempter,
+ NULL);
scheduler.Run();
// Update boot flags after 45 seconds.
diff --git a/udev_interface.h b/udev_interface.h
new file mode 100644
index 0000000..b9765a8
--- /dev/null
+++ b/udev_interface.h
@@ -0,0 +1,193 @@
+// Copyright (c) 2012 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_UDEV_INTERFACE_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UDEV_INTERFACE_H__
+
+#include <libudev.h>
+
+#include "update_engine/utils.h"
+
+// An interface for libudev calls, allowing to easily mock it.
+
+namespace chromeos_update_engine {
+
+// An interface for libudev methods that are being used in update engine.
+//
+// TODO(garnold) As is, this is a pretty lame indirection layer that otherwise
+// does not provide any better abstraction than the existing libudev API. Done
+// properly, we should replace it with encapsulated udev, enumerate and device
+// objects, and hide initialization, reference handling and iterators in ways
+// more appropriate to an object-oriented setting...
+class UdevInterface {
+ public:
+ // Interfaces for various udev closers. All of these are merely containers for
+ // a single pointer to some udev handle, which invoke the provided interface's
+ // unref method and nullify the handle upon destruction. It should suffice for
+ // derivative (concrete) interfaces to implement the various unref methods to
+ // fit their needs, making these closers behave as expected.
+ class UdevCloser {
+ public:
+ explicit UdevCloser(UdevInterface* udev_iface, struct udev** udev_p)
+ : udev_iface_(udev_iface), udev_p_(udev_p) {
+ CHECK(udev_iface && udev_p);
+ }
+ virtual ~UdevCloser() {
+ if (*udev_p_) {
+ udev_iface_->Unref(*udev_p_);
+ *udev_p_ = NULL;
+ }
+ }
+ protected:
+ UdevInterface* udev_iface_;
+ struct udev** udev_p_;
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(UdevCloser);
+ };
+
+ class UdevEnumerateCloser {
+ public:
+ explicit UdevEnumerateCloser(UdevInterface* udev_iface,
+ struct udev_enumerate** udev_enum_p)
+ : udev_iface_(udev_iface), udev_enum_p_(udev_enum_p) {
+ CHECK(udev_iface && udev_enum_p);
+ }
+ virtual ~UdevEnumerateCloser() {
+ if (*udev_enum_p_) {
+ udev_iface_->EnumerateUnref(*udev_enum_p_);
+ *udev_enum_p_ = NULL;
+ }
+ }
+ protected:
+ UdevInterface* udev_iface_;
+ struct udev_enumerate** udev_enum_p_;
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(UdevEnumerateCloser);
+ };
+
+ class UdevDeviceCloser {
+ public:
+ explicit UdevDeviceCloser(UdevInterface* udev_iface,
+ struct udev_device** udev_dev_p)
+ : udev_iface_(udev_iface), udev_dev_p_(udev_dev_p) {
+ CHECK(udev_iface && udev_dev_p);
+ }
+ virtual ~UdevDeviceCloser() {
+ if (*udev_dev_p_) {
+ udev_iface_->DeviceUnref(*udev_dev_p_);
+ *udev_dev_p_ = NULL;
+ }
+ }
+ protected:
+ UdevInterface* udev_iface_;
+ struct udev_device** udev_dev_p_;
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(UdevDeviceCloser);
+ };
+
+ virtual UdevCloser* NewUdevCloser(struct udev** udev_p) {
+ return new UdevCloser(this, udev_p);
+ }
+ virtual UdevEnumerateCloser* NewUdevEnumerateCloser(
+ struct udev_enumerate** udev_enum_p) {
+ return new UdevEnumerateCloser(this, udev_enum_p);
+ }
+ virtual UdevDeviceCloser* NewUdevDeviceCloser(
+ struct udev_device** udev_dev_p) {
+ return new UdevDeviceCloser(this, udev_dev_p);
+ }
+
+ // Lists.
+ virtual const char* ListEntryGetName(struct udev_list_entry* list_entry) = 0;
+ virtual udev_list_entry* ListEntryGetNext(
+ struct udev_list_entry* list_entry) = 0;
+
+ // Device methods.
+ virtual struct udev_device* DeviceNewFromSyspath(
+ struct udev* udev,
+ const char* syspath) = 0;
+ virtual const char* DeviceGetPropertyValue(struct udev_device* udev_device,
+ const char* key) = 0;
+ virtual const char* DeviceGetSyspath(
+ struct udev_device* udev_device) = 0;
+ virtual void DeviceUnref(struct udev_device* udev_device) = 0;
+
+ // Enumerate methods.
+ virtual struct udev_enumerate* EnumerateNew(struct udev* udev) = 0;
+ virtual int EnumerateAddMatchSubsystem(struct udev_enumerate* udev_enum,
+ const char* subsystem) = 0;
+ virtual int EnumerateAddMatchSysname(struct udev_enumerate* udev_enum,
+ const char* sysname) = 0;
+ virtual int EnumerateScanDevices(struct udev_enumerate* udev_enum) = 0;
+ virtual struct udev_list_entry* EnumerateGetListEntry(
+ struct udev_enumerate* udev_enum) = 0;
+ virtual void EnumerateUnref(struct udev_enumerate* udev_enum) = 0;
+
+ // Udev instance methods.
+ virtual struct udev* New() = 0;
+ virtual void Unref(struct udev* udev) = 0;
+};
+
+
+// Implementation of libudev interface using concrete udev calls.
+class StandardUdevInterface : public UdevInterface {
+ public:
+ // Concrete udev API wrappers utilizing the standard libudev calls.
+ virtual const char* ListEntryGetName(struct udev_list_entry* list_entry) {
+ return udev_list_entry_get_name(list_entry);
+ }
+ virtual struct udev_list_entry* ListEntryGetNext(
+ struct udev_list_entry* list_entry) {
+ return udev_list_entry_get_next(list_entry);
+ }
+
+ virtual struct udev_device* DeviceNewFromSyspath(struct udev* udev,
+ const char* syspath) {
+ return udev_device_new_from_syspath(udev, syspath);
+ }
+ virtual const char* DeviceGetPropertyValue(struct udev_device* udev_device,
+ const char* key) {
+ return udev_device_get_property_value(udev_device, key);
+ }
+ virtual const char* DeviceGetSyspath(struct udev_device* udev_device) {
+ return udev_device_get_syspath(udev_device);
+ }
+ virtual void DeviceUnref(struct udev_device* udev_device) {
+ return udev_device_unref(udev_device);
+ }
+
+ virtual struct udev_enumerate* EnumerateNew(struct udev* udev) {
+ return udev_enumerate_new(udev);
+ }
+ virtual int EnumerateAddMatchSubsystem(struct udev_enumerate* udev_enum,
+ const char* subsystem) {
+ return udev_enumerate_add_match_subsystem(udev_enum, subsystem);
+ }
+ virtual int EnumerateAddMatchSysname(struct udev_enumerate* udev_enum,
+ const char* sysname) {
+ return udev_enumerate_add_match_sysname(udev_enum, sysname);
+ }
+ virtual int EnumerateScanDevices(struct udev_enumerate* udev_enum) {
+ return udev_enumerate_scan_devices(udev_enum);
+ }
+ virtual struct udev_list_entry* EnumerateGetListEntry(
+ struct udev_enumerate* udev_enum) {
+ return udev_enumerate_get_list_entry(udev_enum);
+ }
+ virtual void EnumerateUnref(struct udev_enumerate* udev_enum) {
+ return udev_enumerate_unref(udev_enum);
+ }
+
+ virtual struct udev* New() {
+ return udev_new();
+ }
+ virtual void Unref(struct udev* udev) {
+ return udev_unref(udev);
+ }
+};
+
+} // namespace chromeos_update_engine
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_UDEV_INTERFACE_H__
+
diff --git a/update_attempter.cc b/update_attempter.cc
index 214f21a..d6c5496 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -106,7 +106,8 @@
UpdateAttempter::UpdateAttempter(PrefsInterface* prefs,
MetricsLibraryInterface* metrics_lib,
- DbusGlibInterface* dbus_iface)
+ DbusGlibInterface* dbus_iface,
+ GpioHandler* gpio_handler)
: processor_(new ActionProcessor()),
dbus_service_(NULL),
prefs_(prefs),
@@ -129,7 +130,9 @@
update_boot_flags_running_(false),
start_action_processor_(false),
policy_provider_(NULL),
- is_using_test_url_(false) {
+ is_using_test_url_(false),
+ is_test_update_attempted_(false),
+ gpio_handler_(gpio_handler) {
if (utils::FileExists(kUpdateCompletedMarker))
status_ = UPDATE_STATUS_UPDATED_NEED_REBOOT;
}
@@ -331,11 +334,11 @@
// Read GPIO signals and determine whether this is an automated test scenario.
// For safety, we only allow a test update to be performed once; subsequent
// update requests will be carried out normally.
- static bool is_test_used_once = false;
- bool is_test = !is_test_used_once && GpioHandler::IsGpioSignalingTest();
+ bool is_test = (!is_test_update_attempted_ && gpio_handler_ &&
+ gpio_handler_->IsTestModeSignaled());
if (is_test) {
LOG(INFO) << "test mode signaled";
- is_test_used_once = true;
+ is_test_update_attempted_ = true;
}
Update(app_version, omaha_url, true, true, is_test);
diff --git a/update_attempter.h b/update_attempter.h
index 4f30667..950163e 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -61,7 +61,8 @@
UpdateAttempter(PrefsInterface* prefs,
MetricsLibraryInterface* metrics_lib,
- DbusGlibInterface* dbus_iface);
+ DbusGlibInterface* dbus_iface,
+ GpioHandler* gpio_handler);
virtual ~UpdateAttempter();
// Checks for update and, if a newer version is available, attempts to update
@@ -308,6 +309,12 @@
// A flag for indicating whether we are using a test server URL.
bool is_using_test_url_;
+ // A flag indicating whether a test update cycle was already attempted.
+ bool is_test_update_attempted_;
+
+ // GPIO handler object.
+ GpioHandler* gpio_handler_;
+
DISALLOW_COPY_AND_ASSIGN(UpdateAttempter);
};
diff --git a/update_attempter_mock.h b/update_attempter_mock.h
index 50b5c25..bdb83ff 100644
--- a/update_attempter_mock.h
+++ b/update_attempter_mock.h
@@ -15,7 +15,7 @@
class UpdateAttempterMock : public UpdateAttempter {
public:
explicit UpdateAttempterMock(MockDbusGlib* dbus)
- : UpdateAttempter(NULL, NULL, dbus) {}
+ : UpdateAttempter(NULL, NULL, dbus, NULL) {}
MOCK_METHOD5(Update, void(const std::string& app_version,
const std::string& omaha_url,
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 6402bb6..04648e3 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -36,7 +36,7 @@
class UpdateAttempterUnderTest : public UpdateAttempter {
public:
explicit UpdateAttempterUnderTest(MockDbusGlib* dbus)
- : UpdateAttempter(NULL, NULL, dbus) {}
+ : UpdateAttempter(NULL, NULL, dbus, NULL) {}
};
class UpdateAttempterTest : public ::testing::Test {
@@ -120,7 +120,7 @@
OmahaResponse response;
response.poll_interval = 234;
action.SetOutputObject(response);
- UpdateCheckScheduler scheduler(&attempter_);
+ UpdateCheckScheduler scheduler(&attempter_, NULL);
attempter_.set_update_check_scheduler(&scheduler);
EXPECT_CALL(prefs_, GetInt64(kPrefsDeltaUpdateFailures, _)).Times(0);
attempter_.ActionCompleted(NULL, &action, kActionCodeSuccess);
@@ -359,7 +359,7 @@
}
TEST_F(UpdateAttempterTest, PingOmahaTest) {
- UpdateCheckScheduler scheduler(&attempter_);
+ UpdateCheckScheduler scheduler(&attempter_, NULL);
scheduler.enabled_ = true;
EXPECT_FALSE(scheduler.scheduled_);
attempter_.set_update_check_scheduler(&scheduler);
diff --git a/update_check_scheduler.cc b/update_check_scheduler.cc
index cbe90c4..c3b0d17 100644
--- a/update_check_scheduler.cc
+++ b/update_check_scheduler.cc
@@ -18,12 +18,15 @@
const int UpdateCheckScheduler::kTimeoutMaxBackoffInterval = 4 * 60 * 60;
const int UpdateCheckScheduler::kTimeoutRegularFuzz = 10 * 60;
-UpdateCheckScheduler::UpdateCheckScheduler(UpdateAttempter* update_attempter)
+UpdateCheckScheduler::UpdateCheckScheduler(UpdateAttempter* update_attempter,
+ GpioHandler* gpio_handler)
: update_attempter_(update_attempter),
enabled_(false),
scheduled_(false),
last_interval_(0),
- poll_interval_(0) {}
+ poll_interval_(0),
+ is_test_update_attempted_(false),
+ gpio_handler_(gpio_handler) {}
UpdateCheckScheduler::~UpdateCheckScheduler() {}
@@ -88,14 +91,15 @@
CHECK(me->scheduled_);
me->scheduled_ = false;
- static bool is_test_used_once = false;
bool is_test = false;
if (me->IsOOBEComplete() ||
- (is_test = !is_test_used_once && GpioHandler::IsGpioSignalingTest())) {
+ (is_test = (!me->is_test_update_attempted_ &&
+ me->gpio_handler_ &&
+ me->gpio_handler_->IsTestModeSignaled()))) {
if (is_test) {
LOG(WARNING)
<< "test mode signaled, allowing update check prior to OOBE complete";
- is_test_used_once = true;
+ me->is_test_update_attempted_ = true;
}
// Before updating, we flush any previously generated UMA reports.
diff --git a/update_check_scheduler.h b/update_check_scheduler.h
index edfbbfd..cd3ad87 100644
--- a/update_check_scheduler.h
+++ b/update_check_scheduler.h
@@ -41,7 +41,8 @@
static const int kTimeoutRegularFuzz;
static const int kTimeoutMaxBackoffInterval;
- UpdateCheckScheduler(UpdateAttempter* update_attempter);
+ UpdateCheckScheduler(UpdateAttempter* update_attempter,
+ GpioHandler* gpio_handler);
virtual ~UpdateCheckScheduler();
// Initiates the periodic update checks, if necessary.
@@ -128,6 +129,12 @@
// Server dictated poll interval in seconds, if positive.
int poll_interval_;
+ // A flag indicating whether a test update cycle was already attempted.
+ bool is_test_update_attempted_;
+
+ // GPIO handler object.
+ GpioHandler* gpio_handler_;
+
DISALLOW_COPY_AND_ASSIGN(UpdateCheckScheduler);
};
diff --git a/update_check_scheduler_unittest.cc b/update_check_scheduler_unittest.cc
index 098651b..965ce9d 100644
--- a/update_check_scheduler_unittest.cc
+++ b/update_check_scheduler_unittest.cc
@@ -30,7 +30,7 @@
class UpdateCheckSchedulerUnderTest : public UpdateCheckScheduler {
public:
UpdateCheckSchedulerUnderTest(UpdateAttempter* update_attempter)
- : UpdateCheckScheduler(update_attempter) {}
+ : UpdateCheckScheduler(update_attempter, NULL) {}
MOCK_METHOD2(GTimeoutAddSeconds, guint(guint seconds, GSourceFunc function));
MOCK_METHOD0(IsBootDeviceRemovable, bool());