Enable test mode updates.

* Uses the GPIO module to deduce whether a current update session needs
  to be treated as a controlled test, which allows a few relaxations.

* LibcurlHttpFetcher is extended to relax some of its security lock down
  provisions.

* Fix: a test mode flag remains persistent throughout an update attempt,
  so that it can be delegated to the various HttpFetcher instances used
  in the same attempt.

BUG=chromium-os:25397
TEST=Builds and unittests; automated test script works w/ servo
connected Alex

Change-Id: I8a29d1a21a0632912c10f01f69a26d9c659472fd
Reviewed-on: https://gerrit.chromium.org/gerrit/25128
Reviewed-by: Gilad Arnold <garnold@chromium.org>
Tested-by: Gilad Arnold <garnold@chromium.org>
Commit-Ready: Gilad Arnold <garnold@chromium.org>
diff --git a/http_fetcher_unittest.cc b/http_fetcher_unittest.cc
index a46ad2c..9750067 100644
--- a/http_fetcher_unittest.cc
+++ b/http_fetcher_unittest.cc
@@ -238,7 +238,7 @@
     proxy_resolver_.set_num_proxies(num_proxies);
     LibcurlHttpFetcher *ret = new
         LibcurlHttpFetcher(reinterpret_cast<ProxyResolver*>(&proxy_resolver_),
-                           &mock_system_state_);
+                           &mock_system_state_, false);
     // Speed up test execution.
     ret->set_idle_seconds(1);
     ret->set_retry_seconds(1);
@@ -285,8 +285,9 @@
     proxy_resolver_.set_num_proxies(num_proxies);
     ProxyResolver* resolver =
         reinterpret_cast<ProxyResolver*>(&proxy_resolver_);
-    MultiRangeHttpFetcher *ret = new MultiRangeHttpFetcher(
-        new LibcurlHttpFetcher(resolver, &mock_system_state_));
+    MultiRangeHttpFetcher *ret =
+        new MultiRangeHttpFetcher(
+            new LibcurlHttpFetcher(resolver, &mock_system_state_, false));
     ret->ClearRanges();
     ret->AddRange(0);
     // Speed up test execution.
diff --git a/libcurl_http_fetcher.cc b/libcurl_http_fetcher.cc
index 6153f4c..7c40cba 100644
--- a/libcurl_http_fetcher.cc
+++ b/libcurl_http_fetcher.cc
@@ -171,7 +171,7 @@
   // Security lock-down in official builds: makes sure that peer certificate
   // verification is enabled, restricts the set of trusted certificates,
   // restricts protocols to HTTPS, restricts ciphers to HIGH.
-  if (IsOfficialBuild()) {
+  if (!is_test_mode_ && IsOfficialBuild()) {
     CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_SSL_VERIFYPEER, 1),
              CURLE_OK);
     CHECK_EQ(curl_easy_setopt(curl_handle_,
diff --git a/libcurl_http_fetcher.h b/libcurl_http_fetcher.h
index e317819..981d096 100644
--- a/libcurl_http_fetcher.h
+++ b/libcurl_http_fetcher.h
@@ -31,7 +31,8 @@
   static const int kMaxRetryCountOobeNotComplete;
 
   LibcurlHttpFetcher(ProxyResolver* proxy_resolver,
-                     SystemState* system_state)
+                     SystemState* system_state,
+                     bool is_test_mode)
       : HttpFetcher(proxy_resolver, system_state),
         curl_multi_handle_(NULL),
         curl_handle_(NULL),
@@ -53,7 +54,8 @@
         in_write_callback_(false),
         sent_byte_(false),
         terminate_requested_(false),
-        check_certificate_(CertificateChecker::kNone) {}
+        check_certificate_(CertificateChecker::kNone),
+        is_test_mode_(is_test_mode) {}
 
   // Cleans up all internal state. Does not notify delegate
   ~LibcurlHttpFetcher();
@@ -258,6 +260,9 @@
   // this should be kNone.
   CertificateChecker::ServerToCheck check_certificate_;
 
+  // If true, utilizes a relaxed test mode fetch logic. False by default.
+  bool is_test_mode_;
+
   DISALLOW_COPY_AND_ASSIGN(LibcurlHttpFetcher);
 };
 
diff --git a/main.cc b/main.cc
index 84b6c11..596b04d 100644
--- a/main.cc
+++ b/main.cc
@@ -21,6 +21,7 @@
 #include "update_engine/dbus_constants.h"
 #include "update_engine/dbus_interface.h"
 #include "update_engine/dbus_service.h"
+#include "update_engine/gpio_handler.h"
 #include "update_engine/prefs.h"
 #include "update_engine/subprocess.h"
 #include "update_engine/terminator.h"
@@ -181,13 +182,22 @@
   chromeos_update_engine::CertificateChecker::set_openssl_wrapper(
       &openssl_wrapper);
 
+  // Initialize a GPIO handler. Defer GPIO discovery to ensure the udev has
+  // ample time to export the devices. Also require that test mode is physically
+  // queries at most once and the result cached, for a more consistent update
+  // behavior.
+  chromeos_update_engine::StandardUdevInterface udev_iface;
+  chromeos_update_engine::EintrSafeFileDescriptor file_descriptor;
+  chromeos_update_engine::StandardGpioHandler
+      gpio_handler(&udev_iface, &file_descriptor, true, true);
+
   // Create the update attempter:
   chromeos_update_engine::ConcreteDbusGlib dbus;
   chromeos_update_engine::RealSystemState real_system_state;
   chromeos_update_engine::UpdateAttempter update_attempter(&prefs,
                                                            &metrics_lib,
                                                            &dbus,
-                                                           NULL,
+                                                           &gpio_handler,
                                                            &real_system_state);
 
   // Create the dbus service object:
@@ -201,7 +211,7 @@
 
   // Schedule periodic update checks.
   chromeos_update_engine::UpdateCheckScheduler scheduler(&update_attempter,
-                                                         NULL,
+                                                         &gpio_handler,
                                                          &real_system_state);
   scheduler.Run();
 
diff --git a/update_attempter.cc b/update_attempter.cc
index 7e3f362..93c3c1f 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -134,6 +134,7 @@
       start_action_processor_(false),
       policy_provider_(NULL),
       is_using_test_url_(false),
+      is_test_mode_(false),
       is_test_update_attempted_(false),
       gpio_handler_(gpio_handler),
       init_waiting_period_from_prefs_(true),
@@ -150,7 +151,7 @@
                              const string& omaha_url,
                              bool obey_proxies,
                              bool interactive,
-                             bool is_test,
+                             bool is_test_mode,
                              bool is_user_initiated) {
   chrome_proxy_resolver_.Init();
   fake_update_success_ = false;
@@ -171,7 +172,7 @@
                              omaha_url,
                              obey_proxies,
                              interactive,
-                             is_test,
+                             is_test_mode,
                              is_user_initiated)) {
     return;
   }
@@ -191,10 +192,13 @@
                                             const string& omaha_url,
                                             bool obey_proxies,
                                             bool interactive,
-                                            bool is_test,
+                                            bool is_test_mode,
                                             bool is_user_initiated) {
   http_response_code_ = 0;
 
+  // Set the test mode flag for the current update attempt.
+  is_test_mode_ = is_test_mode;
+
   // Lazy initialize the policy provider, or reload the latest policy data.
   if (!policy_provider_.get()) {
     policy_provider_.reset(new policy::PolicyProvider());
@@ -305,7 +309,7 @@
 
   // Determine whether an alternative test address should be used.
   string omaha_url_to_use = omaha_url;
-  if ((is_using_test_url_ = (omaha_url_to_use.empty() && is_test))) {
+  if ((is_using_test_url_ = (omaha_url_to_use.empty() && is_test_mode_))) {
     omaha_url_to_use = kTestUpdateUrl;
     LOG(INFO) << "using alternative server address: " << omaha_url_to_use;
   }
@@ -359,7 +363,7 @@
 
   // Actions:
   LibcurlHttpFetcher* update_check_fetcher =
-      new LibcurlHttpFetcher(GetProxyResolver(), system_state_);
+      new LibcurlHttpFetcher(GetProxyResolver(), system_state_, is_test_mode_);
   // Try harder to connect to the network, esp when not interactive.
   // See comment in libcurl_http_fetcher.cc.
   update_check_fetcher->set_no_network_max_retries(interactive ? 1 : 3);
@@ -382,10 +386,11 @@
                              new OmahaEvent(
                                  OmahaEvent::kTypeUpdateDownloadStarted),
                              new LibcurlHttpFetcher(GetProxyResolver(),
-                                                    system_state_),
+                                                    system_state_,
+                                                    is_test_mode_),
                              false));
   LibcurlHttpFetcher* download_fetcher =
-      new LibcurlHttpFetcher(GetProxyResolver(), system_state_);
+      new LibcurlHttpFetcher(GetProxyResolver(), system_state_, is_test_mode_);
   download_fetcher->set_check_certificate(CertificateChecker::kDownload);
   shared_ptr<DownloadAction> download_action(
       new DownloadAction(prefs_,
@@ -397,7 +402,8 @@
                              new OmahaEvent(
                                  OmahaEvent::kTypeUpdateDownloadFinished),
                              new LibcurlHttpFetcher(GetProxyResolver(),
-                                                    system_state_),
+                                                    system_state_,
+                                                    is_test_mode_),
                              false));
   shared_ptr<FilesystemCopierAction> filesystem_verifier_action(
       new FilesystemCopierAction(false, true, 0));
@@ -410,7 +416,8 @@
                              &omaha_request_params_,
                              new OmahaEvent(OmahaEvent::kTypeUpdateComplete),
                              new LibcurlHttpFetcher(GetProxyResolver(),
-                                                    system_state_),
+                                                    system_state_,
+                                                    is_test_mode_),
                              false));
 
   download_action->set_delegate(this);
@@ -469,16 +476,16 @@
   // 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.
-  bool is_test = (!is_test_update_attempted_ && gpio_handler_ &&
-                  gpio_handler_->IsTestModeSignaled());
-  if (is_test) {
-    LOG(INFO) << "test mode signaled";
+  bool is_test_mode = (!is_test_update_attempted_ && gpio_handler_ &&
+                       gpio_handler_->IsTestModeSignaled());
+  if (is_test_mode) {
+    LOG(WARNING) << "this is a test mode update attempt!";
     is_test_update_attempted_ = true;
   }
 
   // Passing true for is_user_initiated to indicate that this
   // is not a scheduled update check.
-  Update(app_version, omaha_url, true, true, is_test, is_user_initiated);
+  Update(app_version, omaha_url, true, true, is_test_mode, is_user_initiated);
 }
 
 bool UpdateAttempter::RebootIfNeeded() {
@@ -798,7 +805,8 @@
                              &omaha_request_params_,
                              error_event_.release(),  // Pass ownership.
                              new LibcurlHttpFetcher(GetProxyResolver(),
-                                                    system_state_),
+                                                    system_state_,
+                                                    is_test_mode_),
                              false));
   actions_.push_back(shared_ptr<AbstractAction>(error_event_action));
   processor_->EnqueueAction(error_event_action.get());
@@ -913,7 +921,8 @@
                                &omaha_request_params_,
                                NULL,
                                new LibcurlHttpFetcher(GetProxyResolver(),
-                                                      system_state_),
+                                                      system_state_,
+                                                      is_test_mode_),
                                true));
     actions_.push_back(shared_ptr<OmahaRequestAction>(ping_action));
     processor_->set_delegate(NULL);
diff --git a/update_attempter.h b/update_attempter.h
index 77af499..1ab8352 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -80,7 +80,7 @@
                       const std::string& omaha_url,
                       bool obey_proxies,
                       bool interactive,
-                      bool is_test,
+                      bool is_test_mode,
                       bool is_user_initiated);
 
   // ActionProcessorDelegate methods:
@@ -335,6 +335,9 @@
   // A flag for indicating whether we are using a test server URL.
   bool is_using_test_url_;
 
+  // If true, will induce a test mode update attempt.
+  bool is_test_mode_;
+
   // A flag indicating whether a test update cycle was already attempted.
   bool is_test_update_attempted_;
 
diff --git a/update_check_scheduler.cc b/update_check_scheduler.cc
index be599ab..074cb4a 100644
--- a/update_check_scheduler.cc
+++ b/update_check_scheduler.cc
@@ -90,12 +90,12 @@
   CHECK(me->scheduled_);
   me->scheduled_ = false;
 
-  bool is_test = false;
+  bool is_test_mode = false;
   if (me->system_state_->IsOOBEComplete() ||
-      (is_test = (!me->is_test_update_attempted_ &&
-                  me->gpio_handler_ &&
-                  me->gpio_handler_->IsTestModeSignaled()))) {
-    if (is_test) {
+      (is_test_mode = (!me->is_test_update_attempted_ &&
+                       me->gpio_handler_ &&
+                       me->gpio_handler_->IsTestModeSignaled()))) {
+    if (is_test_mode) {
       LOG(WARNING)
           << "test mode signaled, allowing update check prior to OOBE complete";
       me->is_test_update_attempted_ = true;
@@ -103,7 +103,7 @@
 
     // Before updating, we flush any previously generated UMA reports.
     CertificateChecker::FlushReport();
-    me->update_attempter_->Update("", "", false, false, is_test, false);
+    me->update_attempter_->Update("", "", false, false, is_test_mode, false);
   } else {
     // Skips all automatic update checks if the OOBE process is not complete and
     // schedules a new check as if it is the first one.