Update engine should use the release channel policy if it exists.

The release channel (aka update track) can be specified by a device
policy. When this is the case, the update engine should use the
value specified by the policy instead of the value specified in
/etc/lsb-release.

BUG=chromium-os:17015
TEST=Added two new tests:
- Added test that OmahaRequestParams uses the release channel passed
  in to it when the value is valid, and otherwise uses /etc/lsb-release.
- Added test that the update engine correctly picks up the release
  channel that's specified by the policy.

Change-Id: I2fe03712220bb3286476b12cd1f1b330ad006d7c
Reviewed-on: http://gerrit.chromium.org/gerrit/5072
Tested-by: Patrick Dubroy <dubroy@chromium.org>
Reviewed-by: Andrew de los Reyes <adlr@chromium.org>
diff --git a/SConstruct b/SConstruct
index 10c8654..d91b365 100644
--- a/SConstruct
+++ b/SConstruct
@@ -203,6 +203,7 @@
                        gthread-2.0
                        libpcrecpp
                        metrics
+                       policy
                        protobuf
                        pthread
                        rootdev
diff --git a/omaha_request_params.cc b/omaha_request_params.cc
index 0f5af5b..c4dff9d 100644
--- a/omaha_request_params.cc
+++ b/omaha_request_params.cc
@@ -13,6 +13,7 @@
 #include <vector>
 
 #include <base/file_util.h>
+#include <policy/device_policy.h>
 
 #include "update_engine/simple_key_value_store.h"
 #include "update_engine/utils.h"
@@ -25,8 +26,6 @@
 
 namespace chromeos_update_engine {
 
-const char OmahaRequestParams::kUpdateTrackKey[] = "CHROMEOS_RELEASE_TRACK";
-
 const char* const OmahaRequestParams::kAppId(
     "{87efface-864d-49a5-9bb3-4b050a7c227a}");
 const char* const OmahaRequestParams::kOsPlatform("Chrome OS");
@@ -34,12 +33,15 @@
 const char* const OmahaRequestParams::kUpdateUrl(
     "https://tools.google.com/service/update2");
 
+const char OmahaRequestParams::kUpdateTrackKey[] = "CHROMEOS_RELEASE_TRACK";
+
 OmahaRequestDeviceParams::OmahaRequestDeviceParams() :
     force_lock_down_(false),
     forced_lock_down_(false) {}
 
 bool OmahaRequestDeviceParams::Init(const std::string& in_app_version,
-                                    const std::string& in_update_url) {
+                                    const std::string& in_update_url,
+                                    const std::string& in_release_track) {
   bool stateful_override = !ShouldLockDown();
   os_platform = OmahaRequestParams::kOsPlatform;
   os_version = OmahaRequestParams::kOsVersion;
@@ -53,11 +55,18 @@
                        NULL,
                        stateful_override);
   app_lang = "en-US";
-  app_track = GetLsbValue(
-      kUpdateTrackKey,
-      "",
-      &chromeos_update_engine::OmahaRequestDeviceParams::IsValidTrack,
-      true);  // stateful_override
+
+  // Determine the release track if it wasn't specified by the caller.
+  if (in_release_track.empty() || !IsValidTrack(in_release_track)) {
+    app_track = GetLsbValue(
+        kUpdateTrackKey,
+        "",
+        &chromeos_update_engine::OmahaRequestDeviceParams::IsValidTrack,
+        true);  // stateful_override
+  } else {
+    app_track = in_release_track;
+  }
+
   hardware_class = utils::GetHardwareClass();
   struct stat stbuf;
 
@@ -113,7 +122,7 @@
 
 bool OmahaRequestDeviceParams::SetDeviceTrack(const std::string& track) {
   OmahaRequestDeviceParams params;
-  TEST_AND_RETURN_FALSE(params.Init("", ""));
+  TEST_AND_RETURN_FALSE(params.Init("", "", ""));
   return params.SetTrack(track);
 }
 
@@ -121,7 +130,7 @@
   OmahaRequestDeviceParams params;
   // Note that params.app_track is an empty string if the value in
   // lsb-release file is invalid. See Init() for details.
-  return params.Init("", "") ? params.app_track : "";
+  return params.Init("", "", "") ? params.app_track : "";
 }
 
 string OmahaRequestDeviceParams::GetLsbValue(const string& key,
diff --git a/omaha_request_params.h b/omaha_request_params.h
index edaba67..da091c0 100644
--- a/omaha_request_params.h
+++ b/omaha_request_params.h
@@ -73,7 +73,8 @@
   // |in_app_version| or |in_update_url| prevents automatic detection
   // of the parameter. Returns true on success, false otherwise.
   bool Init(const std::string& in_app_version,
-            const std::string& in_update_url);
+            const std::string& in_update_url,
+            const std::string& in_release_track);
 
   // Permanently changes the release track to |track|. Returns true on success,
   // false otherwise.
diff --git a/omaha_request_params_unittest.cc b/omaha_request_params_unittest.cc
index 458f582..d59afcd 100644
--- a/omaha_request_params_unittest.cc
+++ b/omaha_request_params_unittest.cc
@@ -47,7 +47,7 @@
 bool OmahaRequestDeviceParamsTest::DoTest(OmahaRequestParams* out,
                                           const string& app_version,
                                           const string& omaha_url) {
-  bool success = params_.Init(app_version, omaha_url);
+  bool success = params_.Init(app_version, omaha_url, "");
   if (out)
     *out = params_;
   return success;
@@ -326,7 +326,7 @@
     OmahaRequestDeviceParams params;
     params.set_root(string("./") + kTestDir);
     params.SetLockDown(false);
-    EXPECT_TRUE(params.Init("", ""));
+    EXPECT_TRUE(params.Init("", "", ""));
     params.SetTrack("zootrack");
   }
   OmahaRequestParams out;
@@ -351,7 +351,7 @@
     OmahaRequestDeviceParams params;
     params.set_root(string("./") + kTestDir);
     params.SetLockDown(false);
-    EXPECT_TRUE(params.Init("", ""));
+    EXPECT_TRUE(params.Init("", "", ""));
     params.SetTrack("zootrack");
   }
   OmahaRequestParams out;
@@ -372,7 +372,7 @@
     OmahaRequestDeviceParams params;
     params.set_root(string("./") + kTestDir);
     params.SetLockDown(true);
-    EXPECT_TRUE(params.Init("", ""));
+    EXPECT_TRUE(params.Init("", "", ""));
     params.SetTrack("zootrack");
   }
   OmahaRequestParams out;
@@ -421,6 +421,28 @@
   EXPECT_EQ("http://www.google.com", out.update_url);
 }
 
+TEST_F(OmahaRequestDeviceParamsTest, ChannelSpecified) {
+  ASSERT_TRUE(WriteFileString(
+      kTestDir + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=arm-generic\n"
+      "CHROMEOS_RELEASE_FOO=bar\n"
+      "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel\n"
+      "CHROMEOS_AUSERVER=http://www.google.com"));
+  params_.SetLockDown(true);
+  // Passed-in value for release channel should be used.
+  params_.Init("", "", "beta-channel");
+  EXPECT_EQ("beta-channel", params_.app_track);
+
+  // When passed-in value is invalid, value from lsb-release should be used.
+  params_.Init("", "", "foo-channel");
+  EXPECT_EQ("dev-channel", params_.app_track);
+
+  // When passed-in value is empty, value from lsb-release should be used.
+  params_.Init("", "", "");
+  EXPECT_EQ("dev-channel", params_.app_track);
+}
+
 TEST_F(OmahaRequestDeviceParamsTest, ShouldLockDownTest) {
   EXPECT_FALSE(params_.ShouldLockDown());
 }
diff --git a/update_attempter.cc b/update_attempter.cc
index 3206067..27d3a91 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -17,6 +17,8 @@
 #include <base/rand_util.h>
 #include <glib.h>
 #include <metrics/metrics_library.h>
+#include <policy/libpolicy.h>
+#include <policy/device_policy.h>
 
 #include "update_engine/dbus_service.h"
 #include "update_engine/download_action.h"
@@ -119,7 +121,8 @@
       chrome_proxy_resolver_(dbus_iface),
       updated_boot_flags_(false),
       update_boot_flags_running_(false),
-      start_action_processor_(false) {
+      start_action_processor_(false),
+      policy_provider_(NULL) {
   if (utils::FileExists(kUpdateCompletedMarker))
     status_ = UPDATE_STATUS_UPDATED_NEED_REBOOT;
 }
@@ -147,7 +150,20 @@
     return;
   }
   http_response_code_ = 0;
-  if (!omaha_request_params_.Init(app_version, omaha_url)) {
+
+  // Lazy initialize the policy provider, or reload the latest policy data.
+  if (!policy_provider_.get()) {
+    policy_provider_.reset(new policy::PolicyProvider());
+  } else {
+    policy_provider_->Reload();
+  }
+
+  // If the release_track is specified by policy, that takes precedence.
+  string release_track;
+  if (policy_provider_->device_policy_is_loaded())
+    policy_provider_->GetDevicePolicy().GetReleaseChannel(&release_track);
+
+  if (!omaha_request_params_.Init(app_version, omaha_url, release_track)) {
     LOG(ERROR) << "Unable to initialize Omaha request device params.";
     return;
   }
diff --git a/update_attempter.h b/update_attempter.h
index 726685e..46dd41c 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -25,6 +25,10 @@
 class MetricsLibraryInterface;
 struct UpdateEngineService;
 
+namespace policy {
+  class PolicyProvider;
+}
+
 namespace chromeos_update_engine {
 
 class UpdateCheckScheduler;
@@ -140,6 +144,7 @@
   FRIEND_TEST(UpdateAttempterTest, CreatePendingErrorEventResumedTest);
   FRIEND_TEST(UpdateAttempterTest, DisableDeltaUpdateIfNeededTest);
   FRIEND_TEST(UpdateAttempterTest, MarkDeltaUpdateFailureTest);
+  FRIEND_TEST(UpdateAttempterTest, ReadTrackFromPolicy);
   FRIEND_TEST(UpdateAttempterTest, PingOmahaTest);
   FRIEND_TEST(UpdateAttempterTest, ScheduleErrorEventActionNoEventTest);
   FRIEND_TEST(UpdateAttempterTest, ScheduleErrorEventActionTest);
@@ -286,6 +291,9 @@
   // True if the action processor needs to be started by the boot flag updater.
   bool start_action_processor_;
 
+  // Used for fetching information about the device policy.
+  scoped_ptr<policy::PolicyProvider> policy_provider_;
+
   DISALLOW_COPY_AND_ASSIGN(UpdateAttempter);
 };
 
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index b61377b..673471c 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -4,6 +4,8 @@
 
 #include <base/file_util.h>
 #include <gtest/gtest.h>
+#include <policy/libpolicy.h>
+#include <policy/mock_device_policy.h>
 
 #include "update_engine/action_mock.h"
 #include "update_engine/action_processor_mock.h"
@@ -60,14 +62,19 @@
     attempter_.prefs_ = &prefs_;
   }
 
+  void QuitMainLoop();
+  static gboolean StaticQuitMainLoop(gpointer data);
+
   void UpdateTestStart();
   void UpdateTestVerify();
   static gboolean StaticUpdateTestStart(gpointer data);
   static gboolean StaticUpdateTestVerify(gpointer data);
+
   void PingOmahaTestStart();
-  void PingOmahaTestDone();
   static gboolean StaticPingOmahaTestStart(gpointer data);
-  static gboolean StaticPingOmahaTestDone(gpointer data);
+
+  void ReadTrackFromPolicyTestStart();
+  static gboolean StaticReadTrackFromPolicyTestStart(gpointer data);
 
   MockDbusGlib dbus_;
   UpdateAttempterUnderTest attempter_;
@@ -237,6 +244,15 @@
                UpdateStatusToString(static_cast<UpdateStatus>(-1)));
 }
 
+void UpdateAttempterTest::QuitMainLoop() {
+  g_main_loop_quit(loop_);
+}
+
+gboolean UpdateAttempterTest::StaticQuitMainLoop(gpointer data) {
+  reinterpret_cast<UpdateAttempterTest*>(data)->QuitMainLoop();
+  return FALSE;
+}
+
 gboolean UpdateAttempterTest::StaticUpdateTestStart(gpointer data) {
   reinterpret_cast<UpdateAttempterTest*>(data)->UpdateTestStart();
   return FALSE;
@@ -252,8 +268,9 @@
   return FALSE;
 }
 
-gboolean UpdateAttempterTest::StaticPingOmahaTestDone(gpointer data) {
-  reinterpret_cast<UpdateAttempterTest*>(data)->PingOmahaTestDone();
+gboolean UpdateAttempterTest::StaticReadTrackFromPolicyTestStart(
+    gpointer data) {
+  reinterpret_cast<UpdateAttempterTest*>(data)->ReadTrackFromPolicyTestStart();
   return FALSE;
 }
 
@@ -320,11 +337,7 @@
       .Times(1);
   EXPECT_CALL(*processor_, StartProcessing()).Times(1);
   attempter_.PingOmaha();
-  g_idle_add(&StaticPingOmahaTestDone, this);
-}
-
-void UpdateAttempterTest::PingOmahaTestDone() {
-  g_main_loop_quit(loop_);
+  g_idle_add(&StaticQuitMainLoop, this);
 }
 
 TEST_F(UpdateAttempterTest, PingOmahaTest) {
@@ -366,4 +379,32 @@
             attempter_.error_event_->error_code);
 }
 
+TEST_F(UpdateAttempterTest, ReadTrackFromPolicy) {
+  loop_ = g_main_loop_new(g_main_context_default(), FALSE);
+  g_idle_add(&StaticReadTrackFromPolicyTestStart, this);
+  g_main_loop_run(loop_);
+  g_main_loop_unref(loop_);
+  loop_ = NULL;
+}
+
+void UpdateAttempterTest::ReadTrackFromPolicyTestStart() {
+  // Tests that the update track (aka release channel) is properly fetched
+  // from the device policy.
+
+  policy::MockDevicePolicy* device_policy = new policy::MockDevicePolicy();
+  attempter_.policy_provider_.reset(new policy::PolicyProvider(device_policy));
+
+  EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
+
+  EXPECT_CALL(*device_policy, GetReleaseChannel(_))
+      .WillRepeatedly(DoAll(
+          SetArgumentPointee<0>(std::string("canary-channel")),
+          Return(true)));
+
+  attempter_.Update("", "", false, false);
+  EXPECT_EQ("canary-channel", attempter_.omaha_request_params_.app_track);
+
+  g_idle_add(&StaticQuitMainLoop, this);
+}
+
 }  // namespace chromeos_update_engine