Enhanced channel changing behavior

This CL adds a new DBUS API to UpdateEngine called SetTargetChannel to
change the current channel of the device with an option to indicate
whether to do eventually or immediately.

The API will be called with the option to do it immediately in a
subsequent CL in Chrome UI. For now the old API (set_track) has been
wired up to call the new API to produce the old behavior (i.e. change
eventually). The old API will be removed after Chrome UI code stops
using it.

It's the UI's responsibility to ask the user for confirmation for the
powerwash that may happen in some cases and call the API with the
appropriate value whether or not the powerwash should happen.

For now, we're restricting the changing of channels to only those
devices that are on canary-channel or running test builds. This
restriction will be lifted off once the UI work is ready to give
warning to the users about the powerwash that may happen when they move
to a more stable channel.

We also enforce ReleaseChannelDelegated and ReleaseChannel policies
correctly now as follows:

* If ReleaseChannelDelegated is false, SetTargetChannel will fail as we
need to honor (only) the ReleaseChannel value in this case.
* If ReleaseChannelDelegated is true, we'll allow the SetTargetChannel
call to specify. In this case, we'll ignore the value of ReleaseChannel,
if any.

BUG=chromium-os:39095
TEST=Tested on ZGB by going from canary to dev-channel with and without
powerwash.
TEST=Existing unit tests have been updated and they pass.
TEST=New unit tests have been added.

Change-Id: Ifbf806a06e1c30d2f318e94d73735d1812049abd
Reviewed-on: https://gerrit.chromium.org/gerrit/44619
Commit-Queue: Jay Srinivasan <jaysri@chromium.org>
Reviewed-by: Jay Srinivasan <jaysri@chromium.org>
Tested-by: Jay Srinivasan <jaysri@chromium.org>
diff --git a/SConstruct b/SConstruct
index cbdf9de..5a7ad9b 100644
--- a/SConstruct
+++ b/SConstruct
@@ -263,6 +263,7 @@
                    graph_utils.cc
                    http_common.cc
                    http_fetcher.cc
+                   install_plan.cc
                    libcurl_http_fetcher.cc
                    marshal.glibmarshal.c
                    metadata.cc
diff --git a/action_processor.h b/action_processor.h
index 58616b5..7d2e9f8 100644
--- a/action_processor.h
+++ b/action_processor.h
@@ -64,6 +64,7 @@
   kActionCodeDownloadOperationHashMissingError = 38,
   kActionCodeDownloadMetadataSignatureMissingError = 39,
   kActionCodeOmahaUpdateDeferredForBackoff = 40,
+  kActionCodePostinstallPowerwashError = 41,
 
   // Note: When adding new error codes, please remember to add the
   // error into one of the buckets in PayloadState::UpdateFailed method so
diff --git a/dbus_service.cc b/dbus_service.cc
index 7d3d39d..b4cc7c8 100644
--- a/dbus_service.cc
+++ b/dbus_service.cc
@@ -94,16 +94,16 @@
   LOG(INFO) << "Attempt update: app_version=\"" << update_app_version << "\" "
             << "omaha_url=\"" << update_omaha_url << "\" "
             << "interactive=" << (interactive? "yes" : "no");
-  self->update_attempter_->CheckForUpdate(update_app_version,
-                                          update_omaha_url,
-                                          interactive);
+  self->system_state_->update_attempter()->CheckForUpdate(update_app_version,
+                                                          update_omaha_url,
+                                                          interactive);
   return TRUE;
 }
 
 gboolean update_engine_service_reset_status(UpdateEngineService* self,
                                             GError **error) {
   *error = NULL;
-  return self->update_attempter_->ResetStatus();
+  return self->system_state_->update_attempter()->ResetStatus();
 }
 
 
@@ -117,11 +117,11 @@
   string current_op;
   string new_version_str;
 
-  CHECK(self->update_attempter_->GetStatus(last_checked_time,
-                                           progress,
-                                           &current_op,
-                                           &new_version_str,
-                                           new_size));
+  CHECK(self->system_state_->update_attempter()->GetStatus(last_checked_time,
+                                                           progress,
+                                                           &current_op,
+                                                           &new_version_str,
+                                                           new_size));
 
   *current_operation = g_strdup(current_op.c_str());
   *new_version = g_strdup(new_version_str.c_str());
@@ -132,18 +132,9 @@
   return TRUE;
 }
 
-gboolean update_engine_service_get_track(UpdateEngineService* self,
-                                         gchar** track,
-                                         GError **error) {
-  string track_str =
-      chromeos_update_engine::OmahaRequestDeviceParams::GetDeviceTrack();
-  *track = g_strdup(track_str.c_str());
-  return TRUE;
-}
-
 gboolean update_engine_service_reboot_if_needed(UpdateEngineService* self,
                                                 GError **error) {
-  if (!self->update_attempter_->RebootIfNeeded()) {
+  if (!self->system_state_->update_attempter()->RebootIfNeeded()) {
     *error = NULL;
     return FALSE;
   }
@@ -153,14 +144,62 @@
 gboolean update_engine_service_set_track(UpdateEngineService* self,
                                          gchar* track,
                                          GError **error) {
-  if (track) {
-    LOG(INFO) << "Setting track to: " << track;
-    if (!chromeos_update_engine::OmahaRequestDeviceParams::SetDeviceTrack(
-            track)) {
-      *error = NULL;
-      return FALSE;
-    }
+  // track == target channel.
+  return update_engine_service_set_channel(self, track, false, error);
+}
+
+gboolean update_engine_service_get_track(UpdateEngineService* self,
+                                         gchar** track,
+                                         GError **error) {
+  // track == target channel.
+  return update_engine_service_get_channel(self, false, track, error);
+}
+
+gboolean update_engine_service_set_channel(UpdateEngineService* self,
+                                           gchar* target_channel,
+                                           bool is_powerwash_allowed,
+                                           GError **error) {
+  if (!target_channel)
+    return FALSE;
+
+  if (!self->system_state_->device_policy()) {
+    LOG(INFO) << "Cannot set target channel until device policy/settings are "
+                 "known";
+    return FALSE;
   }
+
+  bool delegated = false;
+  self->system_state_->device_policy()->GetReleaseChannelDelegated(&delegated);
+  if (!delegated) {
+    // Note: This message will appear in UE logs with the current UI code
+    // because UI hasn't been modified to call this method only if
+    // delegated is set to true. chromium-os:219292 tracks this work item.
+    LOG(INFO) << "Cannot set target channel explicitly when channel "
+                 "policy/settings is not delegated";
+    return FALSE;
+  }
+
+  LOG(INFO) << "Setting destination channel to: " << target_channel;
+  if (!self->system_state_->request_params()->SetTargetChannel(
+          target_channel, is_powerwash_allowed)) {
+    *error = NULL;
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+gboolean update_engine_service_get_channel(UpdateEngineService* self,
+                                           bool get_current_channel,
+                                           gchar** channel,
+                                           GError **error) {
+  chromeos_update_engine::OmahaRequestParams* rp =
+      self->system_state_->request_params();
+
+  string channel_str = get_current_channel ?
+      rp->current_channel() : rp->target_channel();
+
+  *channel = g_strdup(channel_str.c_str());
   return TRUE;
 }
 
diff --git a/dbus_service.h b/dbus_service.h
index 56e4ef3..71e7997 100644
--- a/dbus_service.h
+++ b/dbus_service.h
@@ -34,7 +34,7 @@
 struct UpdateEngineService {
   GObject parent_instance;
 
-  chromeos_update_engine::UpdateAttempter* update_attempter_;
+  chromeos_update_engine::SystemState* system_state_;
 };
 
 struct UpdateEngineServiceClass {
@@ -62,16 +62,40 @@
                                           int64_t* new_size,
                                           GError **error);
 
+gboolean update_engine_service_reboot_if_needed(UpdateEngineService* self,
+                                                GError **error);
+
+// TODO(jaysri): Deprecate set_track and get_track once Chrome is modified to
+// use set_channel and get_channel.
+gboolean update_engine_service_set_track(UpdateEngineService* self,
+                                         gchar* track,
+                                         GError **error);
+
 gboolean update_engine_service_get_track(UpdateEngineService* self,
                                          gchar** track,
                                          GError **error);
 
-gboolean update_engine_service_reboot_if_needed(UpdateEngineService* self,
-                                                GError **error);
+// Changes the current channel of the device to the target channel. If the
+// target channel is a less stable channel than the current channel, then the
+// channel change happens immediately (at the next update check).  If the
+// target channel is a more stable channel, then if is_powerwash_allowed is set
+// to true, then also the change happens immediately but with a powerwash if
+// required. Otherwise, the change takes effect eventually (when the version on
+// the target channel goes above the version number of what the device
+// currently has).
+gboolean update_engine_service_set_channel(UpdateEngineService* self,
+                                           gchar* target_channel,
+                                           bool is_powerwash_allowed,
+                                           GError **error);
 
-gboolean update_engine_service_set_track(UpdateEngineService* self,
-                                         gchar* track,
-                                         GError **error);
+// If get_current_channel is set to true, populates |channel| with the name of
+// the channel that the device is currently on. Otherwise, it populates it with
+// the name of the channel the device is supposed to be (in case of a pending
+// channel change).
+gboolean update_engine_service_get_channel(UpdateEngineService* self,
+                                           bool get_current_channel,
+                                           gchar** channel,
+                                           GError **error);
 
 gboolean update_engine_service_emit_status_update(
     UpdateEngineService* self,
diff --git a/install_plan.cc b/install_plan.cc
new file mode 100644
index 0000000..59bd5da
--- /dev/null
+++ b/install_plan.cc
@@ -0,0 +1,77 @@
+// Copyright (c) 2013 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/install_plan.h"
+
+#include "base/logging.h"
+
+#include "update_engine/utils.h"
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+InstallPlan::InstallPlan(bool is_resume,
+                         const string& url,
+                         uint64_t payload_size,
+                         const string& payload_hash,
+                         uint64_t metadata_size,
+                         const string& metadata_signature,
+                         const string& install_path,
+                         const string& kernel_install_path)
+    : is_resume(is_resume),
+      download_url(url),
+      payload_size(payload_size),
+      payload_hash(payload_hash),
+      metadata_size(metadata_size),
+      metadata_signature(metadata_signature),
+      install_path(install_path),
+      kernel_install_path(kernel_install_path),
+      kernel_size(0),
+      rootfs_size(0),
+      hash_checks_mandatory(false),
+      powerwash_required(false) {}
+
+InstallPlan::InstallPlan() : is_resume(false),
+                             payload_size(0),
+                             metadata_size(0),
+                             kernel_size(0),
+                             rootfs_size(0),
+                             hash_checks_mandatory(false),
+                             powerwash_required(false) {}
+
+
+bool InstallPlan::operator==(const InstallPlan& that) const {
+  return ((is_resume == that.is_resume) &&
+          (download_url == that.download_url) &&
+          (payload_size == that.payload_size) &&
+          (payload_hash == that.payload_hash) &&
+          (metadata_size == that.metadata_size) &&
+          (metadata_signature == that.metadata_signature) &&
+          (install_path == that.install_path) &&
+          (kernel_install_path == that.kernel_install_path));
+}
+
+bool InstallPlan::operator!=(const InstallPlan& that) const {
+  return !((*this) == that);
+}
+
+void InstallPlan::Dump() const {
+  LOG(INFO) << "InstallPlan: "
+            << (is_resume ? ", resume" : ", new_update")
+            << ", url: " << download_url
+            << ", payload size: " << payload_size
+            << ", payload hash: " << payload_hash
+            << ", metadata size: " << metadata_size
+            << ", metadata signature: " << metadata_signature
+            << ", install_path: " << install_path
+            << ", kernel_install_path: " << kernel_install_path
+            << ", hash_checks_mandatory: " << utils::ToString(
+                hash_checks_mandatory)
+            << ", powerwash_required: " << utils::ToString(
+                powerwash_required);
+}
+
+}  // namespace chromeos_update_engine
+
diff --git a/install_plan.h b/install_plan.h
index c0fa4cd..fc33f25 100644
--- a/install_plan.h
+++ b/install_plan.h
@@ -8,11 +8,10 @@
 #include <string>
 #include <vector>
 
-#include "base/logging.h"
+#include <base/basictypes.h>
 
 // InstallPlan is a simple struct that contains relevant info for many
 // parts of the update system about the install that should happen.
-
 namespace chromeos_update_engine {
 
 struct InstallPlan {
@@ -23,27 +22,16 @@
               uint64_t metadata_size,
               const std::string& metadata_signature,
               const std::string& install_path,
-              const std::string& kernel_install_path)
-      : is_resume(is_resume),
-        download_url(url),
-        payload_size(payload_size),
-        payload_hash(payload_hash),
-        metadata_size(metadata_size),
-        metadata_signature(metadata_signature),
-        install_path(install_path),
-        kernel_install_path(kernel_install_path),
-        kernel_size(0),
-        rootfs_size(0),
-        hash_checks_mandatory(false) {}
+              const std::string& kernel_install_path);
 
   // Default constructor: Initialize all members which don't have a class
   // initializer.
-  InstallPlan() : is_resume(false),
-                  payload_size(0),
-                  metadata_size(0),
-                  kernel_size(0),
-                  rootfs_size(0),
-                  hash_checks_mandatory(false) {}
+  InstallPlan();
+
+  bool operator==(const InstallPlan& that) const;
+  bool operator!=(const InstallPlan& that) const;
+
+  void Dump() const;
 
   bool is_resume;
   std::string download_url;  // url to download from
@@ -75,31 +63,9 @@
   // the Omaha response.
   bool hash_checks_mandatory;
 
-  bool operator==(const InstallPlan& that) const {
-    return ((is_resume == that.is_resume) &&
-            (download_url == that.download_url) &&
-            (payload_size == that.payload_size) &&
-            (payload_hash == that.payload_hash) &&
-            (metadata_size == that.metadata_size) &&
-            (metadata_signature == that.metadata_signature) &&
-            (install_path == that.install_path) &&
-            (kernel_install_path == that.kernel_install_path));
-  }
-  bool operator!=(const InstallPlan& that) const {
-    return !((*this) == that);
-  }
-  void Dump() const {
-    LOG(INFO) << "InstallPlan: "
-              << (is_resume ? ", resume" : ", new_update")
-              << ", url: " << download_url
-              << ", payload size: " << payload_size
-              << ", payload hash: " << payload_hash
-              << ", metadata size: " << metadata_size
-              << ", metadata signature: " << metadata_signature
-              << ", install_path: " << install_path
-              << ", kernel_install_path: " << kernel_install_path
-              << ", hash_checks_mandatory: " << hash_checks_mandatory;
-  }
+  // True if Powerwash is required on reboot after applying the payload.
+  // False otherwise.
+  bool powerwash_required;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/main.cc b/main.cc
index cd51995..47fb934 100644
--- a/main.cc
+++ b/main.cc
@@ -187,7 +187,7 @@
                                   &dbus_glib_update_engine_service_object_info);
   UpdateEngineService* service =
       UPDATE_ENGINE_SERVICE(g_object_new(UPDATE_ENGINE_TYPE_SERVICE, NULL));
-  service->update_attempter_ = update_attempter;
+  service->system_state_ = &real_system_state;
   update_attempter->set_dbus_service(service);
   chromeos_update_engine::SetupDbusService(service);
 
diff --git a/mock_system_state.cc b/mock_system_state.cc
index 13017b9..d25f3c9 100644
--- a/mock_system_state.cc
+++ b/mock_system_state.cc
@@ -9,7 +9,10 @@
 
 // Mock the SystemStateInterface so that we could lie that
 // OOBE is completed even when there's no such marker file, etc.
-MockSystemState::MockSystemState() : prefs_(&mock_prefs_) {
+MockSystemState::MockSystemState()
+  : default_request_params_(this),
+    prefs_(&mock_prefs_) {
+  request_params_ = &default_request_params_;
   mock_payload_state_.Initialize(&mock_prefs_);
   mock_gpio_handler_ = new testing::NiceMock<MockGpioHandler>();
   mock_update_attempter_ = new testing::NiceMock<UpdateAttempterMock>(
diff --git a/mock_system_state.h b/mock_system_state.h
index 956088b..b7f9211 100644
--- a/mock_system_state.h
+++ b/mock_system_state.h
@@ -29,6 +29,7 @@
   virtual ~MockSystemState();
 
   MOCK_METHOD0(IsOOBEComplete, bool());
+
   MOCK_METHOD1(set_device_policy, void(const policy::DevicePolicy*));
   MOCK_CONST_METHOD0(device_policy, const policy::DevicePolicy*());
 
@@ -54,6 +55,10 @@
 
   virtual UpdateAttempter* update_attempter();
 
+  inline virtual OmahaRequestParams* request_params() {
+    return request_params_;
+  }
+
   // MockSystemState-specific public method.
   inline void set_connection_manager(ConnectionManager* connection_manager) {
     connection_manager_ = connection_manager;
@@ -75,6 +80,10 @@
     return &mock_payload_state_;
   }
 
+  inline void set_request_params(OmahaRequestParams* params) {
+    request_params_ = params;
+  }
+
  private:
   // These are Mock objects or objects we own.
   testing::NiceMock<MetricsLibraryMock> mock_metrics_lib_;
@@ -82,12 +91,15 @@
   testing::NiceMock<MockPayloadState> mock_payload_state_;
   testing::NiceMock<MockGpioHandler>* mock_gpio_handler_;
   testing::NiceMock<UpdateAttempterMock>* mock_update_attempter_;
-
   MockDbusGlib dbus_;
 
+  // These are the other object we own.
+  OmahaRequestParams default_request_params_;
+
   // These are pointers to objects which caller can override.
   PrefsInterface* prefs_;
   ConnectionManager* connection_manager_;
+  OmahaRequestParams* request_params_;
 };
 
 } // namespeace chromeos_update_engine
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index b19627b..14ec8f6 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -99,7 +99,7 @@
 
 // Returns an XML ping element if any of the elapsed days need to be
 // sent, or an empty string otherwise.
-string GetPingBody(int ping_active_days, int ping_roll_call_days) {
+string GetPingXml(int ping_active_days, int ping_roll_call_days) {
   string ping_active = GetPingAttribute("a", ping_active_days);
   string ping_roll_call = GetPingAttribute("r", ping_roll_call_days);
   if (!ping_active.empty() || !ping_roll_call.empty()) {
@@ -110,26 +110,27 @@
   return "";
 }
 
-string FormatRequest(const OmahaEvent* event,
-                     const OmahaRequestParams& params,
-                     bool ping_only,
-                     int ping_active_days,
-                     int ping_roll_call_days,
-                     PrefsInterface* prefs) {
-  string body;
+// Returns an XML that goes into the body of the <app> element of the Omaha
+// request based on the given parameters.
+string GetAppBody(const OmahaEvent* event,
+                  const OmahaRequestParams& params,
+                  bool ping_only,
+                  int ping_active_days,
+                  int ping_roll_call_days,
+                  PrefsInterface* prefs) {
+  string app_body;
   if (event == NULL) {
-    body = GetPingBody(ping_active_days, ping_roll_call_days);
+    app_body = GetPingXml(ping_active_days, ping_roll_call_days);
     if (!ping_only) {
       // not passing update_disabled to Omaha because we want to
       // get the update and report with UpdateDeferred result so that
       // borgmon charts show up updates that are deferred. This is also
       // the expected behavior when we move to Omaha v3.0 protocol, so it'll
       // be consistent.
-      body += StringPrintf(
-          "        <updatecheck"
-          " targetversionprefix=\"%s\""
+      app_body += StringPrintf(
+          "        <updatecheck targetversionprefix=\"%s\""
           "></updatecheck>\n",
-          XmlEncode(params.target_version_prefix).c_str());
+          XmlEncode(params.target_version_prefix()).c_str());
 
       // If this is the first update check after a reboot following a previous
       // update, generate an event containing the previous version number. If
@@ -142,16 +143,15 @@
       if (!prefs->GetString(kPrefsPreviousVersion, &prev_version)) {
         prev_version = "0.0.0.0";
       }
-      if (!prev_version.empty()) {
-        body += StringPrintf(
-            "        <event eventtype=\"%d\" eventresult=\"%d\" "
-            "previousversion=\"%s\"></event>\n",
-            OmahaEvent::kTypeUpdateComplete,
-            OmahaEvent::kResultSuccessReboot,
-            XmlEncode(prev_version).c_str());
-        LOG_IF(WARNING, !prefs->SetString(kPrefsPreviousVersion, ""))
-            << "Unable to reset the previous version.";
-      }
+
+      app_body += StringPrintf(
+          "        <event eventtype=\"%d\" eventresult=\"%d\" "
+          "previousversion=\"%s\"></event>\n",
+          OmahaEvent::kTypeUpdateComplete,
+          OmahaEvent::kResultSuccessReboot,
+          XmlEncode(prev_version).c_str());
+      LOG_IF(WARNING, !prefs->SetString(kPrefsPreviousVersion, ""))
+          << "Unable to reset the previous version.";
     }
   } else {
     // The error code is an optional attribute so append it only if the result
@@ -160,29 +160,103 @@
     if (event->result != OmahaEvent::kResultSuccess) {
       error_code = StringPrintf(" errorcode=\"%d\"", event->error_code);
     }
-    body = StringPrintf(
+    app_body = StringPrintf(
         "        <event eventtype=\"%d\" eventresult=\"%d\"%s></event>\n",
         event->type, event->result, error_code.c_str());
   }
-  return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+
+  return app_body;
+}
+
+// Returns an XML that corresponds to the entire <app> node of the Omaha
+// request based on the given parameters.
+string GetAppXml(const OmahaEvent* event,
+                 const OmahaRequestParams& params,
+                 bool ping_only,
+                 int ping_active_days,
+                 int ping_roll_call_days,
+                 SystemState* system_state) {
+  string app_body = GetAppBody(event, params, ping_only, ping_active_days,
+                               ping_roll_call_days, system_state->prefs());
+  string app_versions;
+
+  // If we are upgrading to a more stable channel and we are allowed to do
+  // powerwash, then pass 0.0.0.0 as the version. This is needed to get the
+  // highest-versioned payload on the destination channel.
+  if (params.to_more_stable_channel() && params.is_powerwash_allowed()) {
+    LOG(INFO) << "Passing OS version as 0.0.0.0 as we are set to powerwash "
+              << "on downgrading to the version in the more stable channel";
+    app_versions = "version=\"0.0.0.0\" from_version=\"" +
+      XmlEncode(params.app_version()) + "\" ";
+  } else {
+    app_versions = "version=\"" + XmlEncode(params.app_version()) + "\" ";
+  }
+
+  string app_channels = "track=\"" + XmlEncode(params.target_channel()) + "\" ";
+  if (params.current_channel() != params.target_channel())
+     app_channels +=
+       "from_track=\"" + XmlEncode(params.current_channel()) + "\" ";
+
+  string delta_okay_str = params.delta_okay() ? "true" : "false";
+
+  // Use the default app_id only if we're asking for an update on the
+  // canary-channel. Otherwise, use the board's app_id.
+  string request_app_id =
+      (params.target_channel() == "canary-channel" ?
+       params.app_id() : params.board_app_id());
+  string app_xml =
+      "    <app appid=\"" + XmlEncode(request_app_id) + "\" " +
+                app_versions +
+                app_channels +
+                "lang=\"" + XmlEncode(params.app_lang()) + "\" " +
+                "board=\"" + XmlEncode(params.os_board()) + "\" " +
+                "hardware_class=\"" + XmlEncode(params.hwid()) + "\" " +
+                "delta_okay=\"" + delta_okay_str + "\" "
+                ">\n" +
+                   app_body +
+      "    </app>\n";
+
+  return app_xml;
+}
+
+// Returns an XML that corresponds to the entire <os> node of the Omaha
+// request based on the given parameters.
+string GetOsXml(const OmahaRequestParams& params) {
+  string os_xml =
+      "    <os version=\"" + XmlEncode(params.os_version()) + "\" " +
+               "platform=\"" + XmlEncode(params.os_platform()) + "\" " +
+               "sp=\"" + XmlEncode(params.os_sp()) + "\">"
+          "</os>\n";
+  return os_xml;
+}
+
+// Returns an XML that corresponds to the entire Omaha request based on the
+// given parameters.
+string GetRequestXml(const OmahaEvent* event,
+                     const OmahaRequestParams& params,
+                     bool ping_only,
+                     int ping_active_days,
+                     int ping_roll_call_days,
+                     SystemState* system_state) {
+  string os_xml = GetOsXml(params);
+  string app_xml = GetAppXml(event, params, ping_only, ping_active_days,
+                             ping_roll_call_days, system_state);
+
+  string install_source = StringPrintf("installsource=\"%s\" ",
+      (params.interactive() ? "ondemandupdate" : "scheduler"));
+
+  string request_xml =
+      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
       "<request protocol=\"3.0\" "
-      "version=\"" + XmlEncode(kGupdateVersion) + "\" "
-      "updaterversion=\"" + XmlEncode(kGupdateVersion) + "\" "
-      "installsource=\"" +
-      (params.interactive ? "ondemandupdate" : "scheduler") + "\" "
-      "ismachine=\"1\">\n"
-      "    <os version=\"" + XmlEncode(params.os_version) + "\" platform=\"" +
-      XmlEncode(params.os_platform) + "\" sp=\"" +
-      XmlEncode(params.os_sp) + "\"></os>\n"
-      "    <app appid=\"" + XmlEncode(params.app_id) + "\" version=\"" +
-      XmlEncode(params.app_version) + "\" "
-      "lang=\"" + XmlEncode(params.app_lang) + "\" track=\"" +
-      XmlEncode(params.app_track) + "\" board=\"" +
-      XmlEncode(params.os_board) + "\" hardware_class=\"" +
-      XmlEncode(params.hardware_class) + "\" delta_okay=\"" +
-      (params.delta_okay ? "true" : "false") + "\">\n" + body +
-      "    </app>\n"
+                "version=\"" + XmlEncode(kGupdateVersion) + "\" "
+                "updaterversion=\"" + XmlEncode(kGupdateVersion) + "\" " +
+                install_source +
+                "ismachine=\"1\">\n" +
+                  os_xml +
+                  app_xml +
       "</request>\n";
+
+  return request_xml;
 }
 
 }  // namespace {}
@@ -204,17 +278,17 @@
 }
 
 OmahaRequestAction::OmahaRequestAction(SystemState* system_state,
-                                       OmahaRequestParams* params,
                                        OmahaEvent* event,
                                        HttpFetcher* http_fetcher,
                                        bool ping_only)
     : system_state_(system_state),
-      params_(params),
       event_(event),
       http_fetcher_(http_fetcher),
       ping_only_(ping_only),
       ping_active_days_(0),
-      ping_roll_call_days_(0) {}
+      ping_roll_call_days_(0) {
+  params_ = system_state->request_params();
+}
 
 OmahaRequestAction::~OmahaRequestAction() {}
 
@@ -259,18 +333,18 @@
     processor_->ActionComplete(this, kActionCodeSuccess);
     return;
   }
-  string request_post(FormatRequest(event_.get(),
+  string request_post(GetRequestXml(event_.get(),
                                     *params_,
                                     ping_only_,
                                     ping_active_days_,
                                     ping_roll_call_days_,
-                                    system_state_->prefs()));
+                                    system_state_));
 
   http_fetcher_->SetPostData(request_post.data(), request_post.size(),
                              kHttpContentTypeTextXml);
-  LOG(INFO) << "Posting an Omaha request to " << params_->update_url;
+  LOG(INFO) << "Posting an Omaha request to " << params_->update_url();
   LOG(INFO) << "Request: " << request_post;
-  http_fetcher_->BeginTransfer(params_->update_url);
+  http_fetcher_->BeginTransfer(params_->update_url());
 }
 
 void OmahaRequestAction::TerminateProcessing() {
@@ -680,7 +754,7 @@
   if (!ParseResponse(doc.get(), &output_object, &completer))
     return;
 
-  if (params_->update_disabled) {
+  if (params_->update_disabled()) {
     LOG(INFO) << "Ignoring Omaha updates as updates are disabled by policy.";
     output_object.update_exists = false;
     completer.set_code(kActionCodeOmahaUpdateIgnoredPerPolicy);
@@ -724,7 +798,7 @@
   // wall-clock-based-waiting period and then the update-check-based waiting
   // period, if required.
 
-  if (!params_->wall_clock_based_wait_enabled) {
+  if (!params_->wall_clock_based_wait_enabled()) {
     // Wall-clock-based waiting period is not enabled, so no scattering needed.
     return false;
   }
@@ -810,7 +884,7 @@
       output_object->max_days_to_scatter);
 
   LOG(INFO) << "Waiting Period = "
-            << utils::FormatSecs(params_->waiting_period.InSeconds())
+            << utils::FormatSecs(params_->waiting_period().InSeconds())
             << ", Time Elapsed = "
             << utils::FormatSecs(elapsed_time.InSeconds())
             << ", MaxDaysToScatter = "
@@ -842,14 +916,14 @@
 
   // This means we are required to participate in scattering.
   // See if our turn has arrived now.
-  TimeDelta remaining_wait_time = params_->waiting_period - elapsed_time;
+  TimeDelta remaining_wait_time = params_->waiting_period() - elapsed_time;
   if (remaining_wait_time.InSeconds() <= 0) {
     // Yes, it's our turn now.
     LOG(INFO) << "Successfully passed the wall-clock-based-wait.";
 
     // But we can't download until the update-check-count-based wait is also
     // satisfied, so mark it as required now if update checks are enabled.
-    return params_->update_check_count_wait_enabled ?
+    return params_->update_check_count_wait_enabled() ?
               kWallClockWaitDoneButUpdateCheckWaitRequired :
               kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
   }
@@ -878,8 +952,8 @@
     // This file does not exist. This means we haven't started our update
     // check count down yet, so this is the right time to start the count down.
     update_check_count_value = base::RandInt(
-      params_->min_update_checks_needed,
-      params_->max_update_checks_allowed);
+      params_->min_update_checks_needed(),
+      params_->max_update_checks_allowed());
 
     LOG(INFO) << "Randomly picked update check count value = "
               << update_check_count_value;
@@ -901,7 +975,7 @@
   }
 
   if (update_check_count_value < 0 ||
-      update_check_count_value > params_->max_update_checks_allowed) {
+      update_check_count_value > params_->max_update_checks_allowed()) {
     // We err on the side of skipping scattering logic instead of stalling
     // a machine from receiving any updates in case of any unexpected state.
     LOG(ERROR) << "Invalid value for update check count detected. "
diff --git a/omaha_request_action.h b/omaha_request_action.h
index b6e31af..a75139e 100644
--- a/omaha_request_action.h
+++ b/omaha_request_action.h
@@ -119,7 +119,6 @@
   // or
   // OmahaRequestAction(..., NULL, new WhateverHttpFetcher);
   OmahaRequestAction(SystemState* system_state,
-                     OmahaRequestParams* params,
                      OmahaEvent* event,
                      HttpFetcher* http_fetcher,
                      bool ping_only);
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index 5ce87d7..9bdc167 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -35,11 +35,13 @@
 
 namespace chromeos_update_engine {
 
-class OmahaRequestActionTest : public ::testing::Test { };
+class OmahaRequestActionTest : public ::testing::Test {};
 
 namespace {
 
+MockSystemState mock_system_state;
 OmahaRequestParams kDefaultTestParams(
+    &mock_system_state,
     OmahaRequestParams::kOsPlatform,
     OmahaRequestParams::kOsVersion,
     "service_pack",
@@ -213,8 +215,8 @@
   MockSystemState mock_system_state;
   if (prefs)
     mock_system_state.set_prefs(prefs);
+  mock_system_state.set_request_params(&params);
   OmahaRequestAction action(&mock_system_state,
-                            &params,
                             NULL,
                             fetcher,
                             ping_only);
@@ -252,7 +254,8 @@
                                                  http_response.size(),
                                                  NULL);
   MockSystemState mock_system_state;
-  OmahaRequestAction action(&mock_system_state, &params, event, fetcher, false);
+  mock_system_state.set_request_params(&params);
+  OmahaRequestAction action(&mock_system_state, event, fetcher, false);
   OmahaRequestActionTestProcessorDelegate delegate;
   delegate.loop_ = loop;
   ActionProcessor processor;
@@ -315,7 +318,7 @@
 TEST(OmahaRequestActionTest, ValidUpdateBlockedByPolicyTest) {
   OmahaResponse response;
   OmahaRequestParams params = kDefaultTestParams;
-  params.update_disabled = true;
+  params.set_update_disabled(true);
   ASSERT_FALSE(
       TestUpdateCheck(NULL,  // prefs
                       params,
@@ -340,7 +343,7 @@
 TEST(OmahaRequestActionTest, NoUpdatesSentWhenBlockedByPolicyTest) {
   OmahaResponse response;
   OmahaRequestParams params = kDefaultTestParams;
-  params.update_disabled = true;
+  params.set_update_disabled(true);
   ASSERT_TRUE(
       TestUpdateCheck(NULL,  // prefs
                       params,
@@ -356,9 +359,9 @@
 TEST(OmahaRequestActionTest, WallClockBasedWaitAloneCausesScattering) {
   OmahaResponse response;
   OmahaRequestParams params = kDefaultTestParams;
-  params.wall_clock_based_wait_enabled = true;
-  params.update_check_count_wait_enabled = false;
-  params.waiting_period = TimeDelta::FromDays(2);
+  params.set_wall_clock_based_wait_enabled(true);
+  params.set_update_check_count_wait_enabled(false);
+  params.set_waiting_period(TimeDelta::FromDays(2));
 
   string prefs_dir;
   EXPECT_TRUE(utils::MakeTempDirectory("/tmp/ue_ut_prefs.XXXXXX",
@@ -394,12 +397,12 @@
 TEST(OmahaRequestActionTest, NoWallClockBasedWaitCausesNoScattering) {
   OmahaResponse response;
   OmahaRequestParams params = kDefaultTestParams;
-  params.wall_clock_based_wait_enabled = false;
-  params.waiting_period = TimeDelta::FromDays(2);
+  params.set_wall_clock_based_wait_enabled(false);
+  params.set_waiting_period(TimeDelta::FromDays(2));
 
-  params.update_check_count_wait_enabled = true;
-  params.min_update_checks_needed = 1;
-  params.max_update_checks_allowed = 8;
+  params.set_update_check_count_wait_enabled(true);
+  params.set_min_update_checks_needed(1);
+  params.set_max_update_checks_allowed(8);
 
   string prefs_dir;
   EXPECT_TRUE(utils::MakeTempDirectory("/tmp/ue_ut_prefs.XXXXXX",
@@ -435,12 +438,12 @@
 TEST(OmahaRequestActionTest, ZeroMaxDaysToScatterCausesNoScattering) {
   OmahaResponse response;
   OmahaRequestParams params = kDefaultTestParams;
-  params.wall_clock_based_wait_enabled = true;
-  params.waiting_period = TimeDelta::FromDays(2);
+  params.set_wall_clock_based_wait_enabled(true);
+  params.set_waiting_period(TimeDelta::FromDays(2));
 
-  params.update_check_count_wait_enabled = true;
-  params.min_update_checks_needed = 1;
-  params.max_update_checks_allowed = 8;
+  params.set_update_check_count_wait_enabled(true);
+  params.set_min_update_checks_needed(1);
+  params.set_max_update_checks_allowed(8);
 
   string prefs_dir;
   EXPECT_TRUE(utils::MakeTempDirectory("/tmp/ue_ut_prefs.XXXXXX",
@@ -477,12 +480,12 @@
 TEST(OmahaRequestActionTest, ZeroUpdateCheckCountCausesNoScattering) {
   OmahaResponse response;
   OmahaRequestParams params = kDefaultTestParams;
-  params.wall_clock_based_wait_enabled = true;
-  params.waiting_period = TimeDelta();
+  params.set_wall_clock_based_wait_enabled(true);
+  params.set_waiting_period(TimeDelta());
 
-  params.update_check_count_wait_enabled = true;
-  params.min_update_checks_needed = 0;
-  params.max_update_checks_allowed = 0;
+  params.set_update_check_count_wait_enabled(true);
+  params.set_min_update_checks_needed(0);
+  params.set_max_update_checks_allowed(0);
 
   string prefs_dir;
   EXPECT_TRUE(utils::MakeTempDirectory("/tmp/ue_ut_prefs.XXXXXX",
@@ -522,12 +525,12 @@
 TEST(OmahaRequestActionTest, NonZeroUpdateCheckCountCausesScattering) {
   OmahaResponse response;
   OmahaRequestParams params = kDefaultTestParams;
-  params.wall_clock_based_wait_enabled = true;
-  params.waiting_period = TimeDelta();
+  params.set_wall_clock_based_wait_enabled(true);
+  params.set_waiting_period(TimeDelta());
 
-  params.update_check_count_wait_enabled = true;
-  params.min_update_checks_needed = 1;
-  params.max_update_checks_allowed = 8;
+  params.set_update_check_count_wait_enabled(true);
+  params.set_min_update_checks_needed(1);
+  params.set_max_update_checks_allowed(8);
 
   string prefs_dir;
   EXPECT_TRUE(utils::MakeTempDirectory("/tmp/ue_ut_prefs.XXXXXX",
@@ -567,12 +570,12 @@
 TEST(OmahaRequestActionTest, ExistingUpdateCheckCountCausesScattering) {
   OmahaResponse response;
   OmahaRequestParams params = kDefaultTestParams;
-  params.wall_clock_based_wait_enabled = true;
-  params.waiting_period = TimeDelta();
+  params.set_wall_clock_based_wait_enabled(true);
+  params.set_waiting_period(TimeDelta());
 
-  params.update_check_count_wait_enabled = true;
-  params.min_update_checks_needed = 1;
-  params.max_update_checks_allowed = 8;
+  params.set_update_check_count_wait_enabled(true);
+  params.set_min_update_checks_needed(1);
+  params.set_max_update_checks_allowed(8);
 
   string prefs_dir;
   EXPECT_TRUE(utils::MakeTempDirectory("/tmp/ue_ut_prefs.XXXXXX",
@@ -620,7 +623,8 @@
 
   MockSystemState mock_system_state;
   OmahaRequestParams params = kDefaultTestParams;
-  OmahaRequestAction action(&mock_system_state, &params, NULL,
+  mock_system_state.set_request_params(&params);
+  OmahaRequestAction action(&mock_system_state, NULL,
                             new MockHttpFetcher(http_response.data(),
                                                 http_response.size(),
                                                 NULL),
@@ -785,7 +789,8 @@
 
   MockSystemState mock_system_state;
   OmahaRequestParams params = kDefaultTestParams;
-  OmahaRequestAction action(&mock_system_state, &params, NULL,
+  mock_system_state.set_request_params(&params);
+  OmahaRequestAction action(&mock_system_state, NULL,
                             new MockHttpFetcher(http_response.data(),
                                                 http_response.size(),
                                                 NULL),
@@ -811,7 +816,9 @@
   vector<char> post_data;
 
   // Make sure XML Encode is being called on the params
-  OmahaRequestParams params(OmahaRequestParams::kOsPlatform,
+  MockSystemState mock_system_state;
+  OmahaRequestParams params(&mock_system_state,
+                            OmahaRequestParams::kOsPlatform,
                             OmahaRequestParams::kOsVersion,
                             "testtheservice_pack>",
                             "x86 generic<id",
@@ -903,7 +910,7 @@
   NiceMock<PrefsMock> prefs;
   EXPECT_CALL(prefs, GetString(kPrefsPreviousVersion, _))
       .WillOnce(DoAll(SetArgumentPointee<1>(string("")), Return(true)));
-  EXPECT_CALL(prefs, SetString(kPrefsPreviousVersion, _)).Times(0);
+  EXPECT_CALL(prefs, SetString(kPrefsPreviousVersion, _)).Times(1);
   ASSERT_FALSE(TestUpdateCheck(&prefs,
                                kDefaultTestParams,
                                "invalid xml>",
@@ -916,24 +923,21 @@
   string post_str(&post_data[0], post_data.size());
   EXPECT_NE(post_str.find(
       "        <ping active=\"1\" a=\"-1\" r=\"-1\"></ping>\n"
-      "        <updatecheck"
-      " targetversionprefix=\"\""
-      "></updatecheck>\n"),
+      "        <updatecheck targetversionprefix=\"\"></updatecheck>\n"),
       string::npos);
   EXPECT_NE(post_str.find("hardware_class=\"OEM MODEL 09235 7471\""),
             string::npos);
-  EXPECT_EQ(post_str.find("event"), string::npos);
 }
 
 
-TEST(OmahaRequestActionTest, FormatTargetVersionPrefixTest) {
+TEST(OmahaRequestActionTest, FormatUpdateDisabledOutputTest) {
   vector<char> post_data;
   NiceMock<PrefsMock> prefs;
   EXPECT_CALL(prefs, GetString(kPrefsPreviousVersion, _))
       .WillOnce(DoAll(SetArgumentPointee<1>(string("")), Return(true)));
-  EXPECT_CALL(prefs, SetString(kPrefsPreviousVersion, _)).Times(0);
+  EXPECT_CALL(prefs, SetString(kPrefsPreviousVersion, _)).Times(1);
   OmahaRequestParams params = kDefaultTestParams;
-  params.update_disabled = true;
+  params.set_update_disabled(true);
   ASSERT_FALSE(TestUpdateCheck(&prefs,
                                params,
                                "invalid xml>",
@@ -946,13 +950,10 @@
   string post_str(&post_data[0], post_data.size());
   EXPECT_NE(post_str.find(
       "        <ping active=\"1\" a=\"-1\" r=\"-1\"></ping>\n"
-      "        <updatecheck"
-      " targetversionprefix=\"\""
-      "></updatecheck>\n"),
+      "        <updatecheck targetversionprefix=\"\"></updatecheck>\n"),
       string::npos);
   EXPECT_NE(post_str.find("hardware_class=\"OEM MODEL 09235 7471\""),
             string::npos);
-  EXPECT_EQ(post_str.find("event"), string::npos);
 }
 
 TEST(OmahaRequestActionTest, FormatSuccessEventOutputTest) {
@@ -996,9 +997,9 @@
   string http_response("doesn't matter");
   MockSystemState mock_system_state;
   OmahaRequestParams params = kDefaultTestParams;
+  mock_system_state.set_request_params(&params);
   OmahaRequestAction update_check_action(
       &mock_system_state,
-      &params,
       NULL,
       new MockHttpFetcher(http_response.data(),
                           http_response.size(),
@@ -1007,9 +1008,9 @@
   EXPECT_FALSE(update_check_action.IsEvent());
 
   params = kDefaultTestParams;
+  mock_system_state.set_request_params(&params);
   OmahaRequestAction event_action(
       &mock_system_state,
-      &params,
       new OmahaEvent(OmahaEvent::kTypeUpdateComplete),
       new MockHttpFetcher(http_response.data(),
                           http_response.size(),
@@ -1023,7 +1024,9 @@
     bool delta_okay = i == 1;
     const char* delta_okay_str = delta_okay ? "true" : "false";
     vector<char> post_data;
-    OmahaRequestParams params(OmahaRequestParams::kOsPlatform,
+    MockSystemState mock_system_state;
+    OmahaRequestParams params(&mock_system_state,
+                              OmahaRequestParams::kOsPlatform,
                               OmahaRequestParams::kOsVersion,
                               "service_pack",
                               "x86-generic",
@@ -1058,7 +1061,9 @@
     bool interactive = i == 1;
     const char* interactive_str = interactive ? "ondemandupdate" : "scheduler";
     vector<char> post_data;
-    OmahaRequestParams params(OmahaRequestParams::kOsPlatform,
+    MockSystemState mock_system_state;
+    OmahaRequestParams params(&mock_system_state,
+                              OmahaRequestParams::kOsPlatform,
                               OmahaRequestParams::kOsVersion,
                               "service_pack",
                               "x86-generic",
@@ -1382,9 +1387,9 @@
 TEST(OmahaRequestActionTest, TestUpdateFirstSeenAtGetsPersistedFirstTime) {
   OmahaResponse response;
   OmahaRequestParams params = kDefaultTestParams;
-  params.wall_clock_based_wait_enabled = true;
-  params.waiting_period = TimeDelta().FromDays(1);
-  params.update_check_count_wait_enabled = false;
+  params.set_wall_clock_based_wait_enabled(true);
+  params.set_waiting_period(TimeDelta().FromDays(1));
+  params.set_update_check_count_wait_enabled(false);
 
   string prefs_dir;
   EXPECT_TRUE(utils::MakeTempDirectory("/tmp/ue_ut_prefs.XXXXXX",
@@ -1424,9 +1429,9 @@
 TEST(OmahaRequestActionTest, TestUpdateFirstSeenAtGetsUsedIfAlreadyPresent) {
   OmahaResponse response;
   OmahaRequestParams params = kDefaultTestParams;
-  params.wall_clock_based_wait_enabled = true;
-  params.waiting_period = TimeDelta().FromDays(1);
-  params.update_check_count_wait_enabled = false;
+  params.set_wall_clock_based_wait_enabled(true);
+  params.set_waiting_period(TimeDelta().FromDays(1));
+  params.set_update_check_count_wait_enabled(false);
 
   string prefs_dir;
   EXPECT_TRUE(utils::MakeTempDirectory("/tmp/ue_ut_prefs.XXXXXX",
@@ -1470,4 +1475,84 @@
   ASSERT_TRUE(timestamp == t1.ToInternalValue());
 }
 
+TEST(OmahaRequestActionTest, TestChangingToMoreStableChannel) {
+  const string kTestDir = "omaha_request_action-test";
+  ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir + "/etc"));
+  ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir +
+                        utils::kStatefulPartition + "/etc"));
+  vector<char> post_data;
+  NiceMock<PrefsMock> prefs;
+  ASSERT_TRUE(WriteFileString(
+      kTestDir + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_APPID={11111111-1111-1111-1111-111111111111}\n"
+      "CHROMEOS_BOARD_APPID={22222222-2222-2222-2222-222222222222}\n"
+      "CHROMEOS_RELEASE_TRACK=canary-channel\n"));
+  ASSERT_TRUE(WriteFileString(
+      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
+      "CHROMEOS_IS_POWERWASH_ALLOWED=true\n"
+      "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
+  OmahaRequestParams params = kDefaultTestParams;
+  params.set_root(string("./") + kTestDir);
+  params.SetLockDown(false);
+  params.Init("1.2.3.4", "", 0);
+  EXPECT_EQ("canary-channel", params.current_channel());
+  EXPECT_EQ("stable-channel", params.target_channel());
+  EXPECT_TRUE(params.to_more_stable_channel());
+  EXPECT_TRUE(params.is_powerwash_allowed());
+  ASSERT_FALSE(TestUpdateCheck(&prefs,
+                               params,
+                               "invalid xml>",
+                               -1,
+                               false,  // ping_only
+                               kActionCodeOmahaRequestXMLParseError,
+                               NULL,  // response
+                               &post_data));
+  // convert post_data to string
+  string post_str(&post_data[0], post_data.size());
+  EXPECT_NE(string::npos, post_str.find(
+      "appid=\"{22222222-2222-2222-2222-222222222222}\" "
+      "version=\"0.0.0.0\" from_version=\"1.2.3.4\" "
+      "track=\"stable-channel\" from_track=\"canary-channel\" "));
+}
+
+TEST(OmahaRequestActionTest, TestChangingToLessStableChannel) {
+  const string kTestDir = "omaha_request_action-test";
+  ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir + "/etc"));
+  ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir +
+                        utils::kStatefulPartition + "/etc"));
+  vector<char> post_data;
+  NiceMock<PrefsMock> prefs;
+  ASSERT_TRUE(WriteFileString(
+      kTestDir + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_APPID={11111111-1111-1111-1111-111111111111}\n"
+      "CHROMEOS_BOARD_APPID={22222222-2222-2222-2222-222222222222}\n"
+      "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
+  ASSERT_TRUE(WriteFileString(
+      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_TRACK=canary-channel\n"));
+  OmahaRequestParams params = kDefaultTestParams;
+  params.set_root(string("./") + kTestDir);
+  params.SetLockDown(false);
+  params.Init("5.6.7.8", "", 0);
+  EXPECT_EQ("stable-channel", params.current_channel());
+  EXPECT_EQ("canary-channel", params.target_channel());
+  EXPECT_FALSE(params.to_more_stable_channel());
+  EXPECT_FALSE(params.is_powerwash_allowed());
+  ASSERT_FALSE(TestUpdateCheck(&prefs,
+                               params,
+                               "invalid xml>",
+                               -1,
+                               false,  // ping_only
+                               kActionCodeOmahaRequestXMLParseError,
+                               NULL,  // response
+                               &post_data));
+  // convert post_data to string
+  string post_str(&post_data[0], post_data.size());
+  EXPECT_NE(string::npos, post_str.find(
+      "appid=\"{11111111-1111-1111-1111-111111111111}\" "
+      "version=\"5.6.7.8\" "
+      "track=\"canary-channel\" from_track=\"stable-channel\""));
+  EXPECT_EQ(string::npos, post_str.find( "from_version"));
+}
+
 }  // namespace chromeos_update_engine
diff --git a/omaha_request_params.cc b/omaha_request_params.cc
index 46cd5aa..53c0975 100644
--- a/omaha_request_params.cc
+++ b/omaha_request_params.cc
@@ -16,6 +16,7 @@
 #include <policy/device_policy.h>
 
 #include "update_engine/simple_key_value_store.h"
+#include "update_engine/system_state.h"
 #include "update_engine/utils.h"
 
 #define CALL_MEMBER_FN(object, member) ((object).*(member))
@@ -33,115 +34,193 @@
 const char* const kProductionOmahaUrl(
     "https://tools.google.com/service/update2");
 
-const char OmahaRequestParams::kUpdateTrackKey[] = "CHROMEOS_RELEASE_TRACK";
+const char* const OmahaRequestParams::kUpdateChannelKey(
+    "CHROMEOS_RELEASE_TRACK");
+const char* const OmahaRequestParams::kIsPowerwashAllowedKey(
+    "CHROMEOS_IS_POWERWASH_ALLOWED");
 
-OmahaRequestDeviceParams::OmahaRequestDeviceParams() :
-    force_lock_down_(false),
-    forced_lock_down_(false) {}
+const char* kChannelsByStability[] = {
+    // This list has to be sorted from least stable to most stable channel.
+    "canary-channel",
+    "dev-channel",
+    "beta-channel",
+    "stable-channel",
+};
 
-bool OmahaRequestDeviceParams::Init(const std::string& in_app_version,
-                                    const std::string& in_update_url,
-                                    const std::string& in_release_track,
-                                    bool in_interactive) {
+bool OmahaRequestParams::Init(const std::string& in_app_version,
+                              const std::string& in_update_url,
+                              bool in_interactive) {
+  InitFromLsbValue();
   bool stateful_override = !ShouldLockDown();
-  os_platform = OmahaRequestParams::kOsPlatform;
-  os_version = OmahaRequestParams::kOsVersion;
-  app_version = in_app_version.empty() ?
+  os_platform_ = OmahaRequestParams::kOsPlatform;
+  os_version_ = OmahaRequestParams::kOsVersion;
+  app_version_ = in_app_version.empty() ?
       GetLsbValue("CHROMEOS_RELEASE_VERSION", "", NULL, stateful_override) :
       in_app_version;
-  os_sp = app_version + "_" + GetMachineType();
-  os_board = GetLsbValue("CHROMEOS_RELEASE_BOARD", "", NULL, stateful_override);
-  app_id = GetLsbValue("CHROMEOS_RELEASE_APPID",
-                       OmahaRequestParams::kAppId,
-                       NULL,
-                       stateful_override);
-  app_lang = "en-US";
+  os_sp_ = app_version_ + "_" + GetMachineType();
+  os_board_ = GetLsbValue("CHROMEOS_RELEASE_BOARD",
+                          "",
+                          NULL,
+                          stateful_override);
+  app_id_ = GetLsbValue("CHROMEOS_RELEASE_APPID",
+                        OmahaRequestParams::kAppId,
+                        NULL,
+                        stateful_override);
+  board_app_id_ = GetLsbValue("CHROMEOS_BOARD_APPID",
+                              app_id_,
+                              NULL,
+                              stateful_override);
+  app_lang_ = "en-US";
+  hwid_ = utils::GetHardwareClass();
 
-  // 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
+  if (current_channel_ == target_channel_) {
+    // deltas are only okay if the /.nodelta file does not exist.  if we don't
+    // know (i.e. stat() returns some unexpected error), then err on the side of
+    // caution and say deltas are not okay.
+    struct stat stbuf;
+    delta_okay_ = (stat((root_ + "/.nodelta").c_str(), &stbuf) < 0) &&
+                  (errno == ENOENT);
+
   } else {
-    app_track = in_release_track;
+    LOG(INFO) << "Disabling deltas as a channel change is pending";
+    // For now, disable delta updates if the current channel is different from
+    // the channel that we're sending to the update server because such updates
+    // are destined to fail -- the current rootfs hash will be different than
+    // the expected hash due to the different channel in /etc/lsb-release.
+    delta_okay_ = false;
   }
 
-  hardware_class = utils::GetHardwareClass();
-  struct stat stbuf;
-
-  // Deltas are only okay if the /.nodelta file does not exist.  If we don't
-  // know (i.e. stat() returns some unexpected error), then err on the side of
-  // caution and say deltas are not okay.
-  delta_okay = (stat((root_ + "/.nodelta").c_str(), &stbuf) < 0) &&
-               (errno == ENOENT);
-
-  // For now, disable delta updates if the rootfs track is different than the
-  // track that we're sending to the update server because such updates are
-  // destined to fail -- the source rootfs hash will be different than the
-  // expected hash due to the different track in /etc/lsb-release.
-  //
-  // Longer term we should consider an alternative: (a) clients can send
-  // (current_version, current_channel, new_channel) information, or (b) the
-  // build process can make sure releases on separate tracks are identical (i.e,
-  // by not stamping the release with the channel), or (c) the release process
-  // can ensure that different channels get different version numbers.
-  const string rootfs_track = GetLsbValue(
-      kUpdateTrackKey,
-      "",
-      NULL,  // No need to validate the read-only rootfs track.
-      false);  // stateful_override
-  delta_okay = delta_okay && rootfs_track == app_track;
-
-  update_url = in_update_url.empty() ?
-      GetLsbValue("CHROMEOS_AUSERVER",
-                  kProductionOmahaUrl,
-                  NULL,
-                  stateful_override) :
-      in_update_url;
+  if (in_update_url.empty())
+    update_url_ = GetLsbValue("CHROMEOS_AUSERVER", kProductionOmahaUrl, NULL,
+                              stateful_override);
+  else
+    update_url_ = in_update_url;
 
   // Set the interactive flag accordingly.
-  interactive = in_interactive;
-
+  interactive_ = in_interactive;
   return true;
 }
 
-bool OmahaRequestDeviceParams::SetTrack(const std::string& track) {
-  TEST_AND_RETURN_FALSE(IsValidTrack(track));
+bool OmahaRequestParams::SetTargetChannel(const std::string& new_target_channel,
+                                          bool is_powerwash_allowed) {
+  LOG(INFO) << "SetTargetChannel called with " << new_target_channel
+            << ". Is Powerwash Allowed = "
+            << utils::ToString(is_powerwash_allowed);
+
+  // Ignore duplicate calls so we can make the method succeed and be
+  // idempotent so as not to surface unnecessary errors to the UI.
+  if (new_target_channel == target_channel_ &&
+      is_powerwash_allowed == is_powerwash_allowed_) {
+    if (new_target_channel == current_channel_) {
+      // Return true to make such calls no-op and idempotent.
+      LOG(INFO) << "SetTargetChannel: Already on " << current_channel_;
+      return true;
+    }
+
+    LOG(INFO) << "SetTargetChannel: Target channel has already been set";
+    return true;
+  }
+
+  // See if there's a channel change already in progress. If so, don't honor
+  // a new channel change until the existing request is fulfilled.
+  if (current_channel_ != target_channel_) {
+    // Avoid dealing with multiple pending channels as they cause a lot of
+    // edge cases that's not worth adding the complexity for.
+    LOG(ERROR) << "Cannot change to " << new_target_channel
+               << " now as we're currently in " << current_channel_
+               << " and the request to change to " << target_channel_
+               << " is pending";
+    return false;
+  }
+
+  if (current_channel_ == "canary-channel") {
+    // TODO(jaysri): chromium-os:39751: We don't have the UI warnings yet. So,
+    // enable the powerwash-on-changing-to-more-stable-channel behavior for now
+    // only on canary-channel devices.
+    is_powerwash_allowed = true;
+    LOG(INFO) << "Is Powerwash Allowed set to true as we are in canary-channel";
+  } else if (!utils::IsOfficialBuild() &&
+             current_channel_ == "testimage-channel") {
+    // Also, allow test builds to have the powerwash behavior so we can always
+    // test channel changing behavior on them, without having to first get them
+    // on an official channel.
+    is_powerwash_allowed = true;
+    LOG(INFO) << "Is Powerwash Allowed set to true as we are running an "
+                 "unofficial build";
+  }
+
+  TEST_AND_RETURN_FALSE(IsValidChannel(new_target_channel));
   FilePath kFile(root_ + utils::kStatefulPartition + "/etc/lsb-release");
   string file_data;
   map<string, string> data;
   if (file_util::ReadFileToString(kFile, &file_data)) {
     data = simple_key_value_store::ParseString(file_data);
   }
-  data[kUpdateTrackKey] = track;
+  data[kUpdateChannelKey] = new_target_channel;
+  data[kIsPowerwashAllowedKey] = is_powerwash_allowed ? "true" : "false";
   file_data = simple_key_value_store::AssembleString(data);
   TEST_AND_RETURN_FALSE(file_util::CreateDirectory(kFile.DirName()));
   TEST_AND_RETURN_FALSE(
       file_util::WriteFile(kFile, file_data.data(), file_data.size()) ==
       static_cast<int>(file_data.size()));
-  app_track = track;
+  target_channel_ = new_target_channel;
+  is_powerwash_allowed_ = is_powerwash_allowed;
   return true;
 }
 
-bool OmahaRequestDeviceParams::SetDeviceTrack(const std::string& track) {
-  OmahaRequestDeviceParams params;
-  TEST_AND_RETURN_FALSE(params.Init("", "", "", false));
-  return params.SetTrack(track);
+void OmahaRequestParams::SetTargetChannelFromLsbValue() {
+  string target_channel_new_value = GetLsbValue(
+      kUpdateChannelKey,
+      "",
+      &chromeos_update_engine::OmahaRequestParams::IsValidChannel,
+      true);  // stateful_override
+
+  if (target_channel_ != target_channel_new_value) {
+    target_channel_ = target_channel_new_value;
+    LOG(INFO) << "Target Channel set to " << target_channel_
+              << " from LSB file";
+  }
 }
 
-string OmahaRequestDeviceParams::GetDeviceTrack() {
-  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("", "", "", false) ? params.app_track : "";
+void OmahaRequestParams::SetCurrentChannelFromLsbValue() {
+  string current_channel_new_value = GetLsbValue(
+      kUpdateChannelKey,
+      "",
+      NULL,  // No need to validate the read-only rootfs channel.
+      false);  // stateful_override is false so we get the current channel.
+
+  if (current_channel_ != current_channel_new_value) {
+    current_channel_ = current_channel_new_value;
+    LOG(INFO) << "Current Channel set to " << current_channel_
+              << " from LSB file in rootfs";
+  }
 }
 
-string OmahaRequestDeviceParams::GetLsbValue(const string& key,
-                                             const string& default_value,
-                                             ValueValidator validator,
-                                             bool stateful_override) const {
+void OmahaRequestParams::SetIsPowerwashAllowedFromLsbValue() {
+  string is_powerwash_allowed_str = GetLsbValue(
+      kIsPowerwashAllowedKey,
+      "false",
+      NULL, // no need to validate
+      true); // always get it from stateful, as that's the only place it'll be
+  bool is_powerwash_allowed_new_value = (is_powerwash_allowed_str == "true");
+  if (is_powerwash_allowed_ != is_powerwash_allowed_new_value) {
+    is_powerwash_allowed_ = is_powerwash_allowed_new_value;
+    LOG(INFO) << "Powerwash Allowed set to "
+              << utils::ToString(is_powerwash_allowed_)
+              << " from LSB file in stateful";
+  }
+}
+
+void OmahaRequestParams::InitFromLsbValue() {
+  SetTargetChannelFromLsbValue();
+  SetCurrentChannelFromLsbValue();
+  SetIsPowerwashAllowedFromLsbValue();
+}
+
+string OmahaRequestParams::GetLsbValue(const string& key,
+                                       const string& default_value,
+                                       ValueValidator validator,
+                                       bool stateful_override) const {
   vector<string> files;
   if (stateful_override) {
     files.push_back(string(utils::kStatefulPartition) + "/etc/lsb-release");
@@ -168,7 +247,7 @@
   return default_value;
 }
 
-string OmahaRequestDeviceParams::GetMachineType() const {
+string OmahaRequestParams::GetMachineType() const {
   struct utsname buf;
   string ret;
   if (uname(&buf) == 0)
@@ -176,35 +255,40 @@
   return ret;
 }
 
-bool OmahaRequestDeviceParams::ShouldLockDown() const {
+bool OmahaRequestParams::ShouldLockDown() const {
   if (force_lock_down_) {
     return forced_lock_down_;
   }
   return utils::IsOfficialBuild() && utils::IsNormalBootMode();
 }
 
-bool OmahaRequestDeviceParams::IsValidTrack(const std::string& track) const {
-  static const char* kValidTracks[] = {
-    "beta-channel",
-    "canary-channel",
-    "dev-channel",
-    "dogfood-channel",
-    "stable-channel",
-  };
-  if (!ShouldLockDown()) {
-    return true;
-  }
-  for (size_t t = 0; t < arraysize(kValidTracks); ++t) {
-    if (track == kValidTracks[t]) {
-      return true;
-    }
-  }
-  return false;
+bool OmahaRequestParams::IsValidChannel(const std::string& channel) const {
+  return GetChannelIndex(channel) >= 0;
 }
 
-void OmahaRequestDeviceParams::SetLockDown(bool lock) {
+void OmahaRequestParams::set_root(const std::string& root) {
+  root_ = root;
+  InitFromLsbValue();
+}
+
+void OmahaRequestParams::SetLockDown(bool lock) {
   force_lock_down_ = true;
   forced_lock_down_ = lock;
 }
 
+int OmahaRequestParams::GetChannelIndex(const std::string& channel) const {
+  for (size_t t = 0; t < arraysize(kChannelsByStability); ++t)
+    if (channel == kChannelsByStability[t])
+      return t;
+
+  return -1;
+}
+
+bool OmahaRequestParams::to_more_stable_channel() const {
+  int current_channel_index = GetChannelIndex(current_channel_);
+  int target_channel_index = GetChannelIndex(target_channel_);
+
+  return target_channel_index > current_channel_index;
+}
+
 }  // namespace chromeos_update_engine
diff --git a/omaha_request_params.h b/omaha_request_params.h
index f38978c..3420b41 100644
--- a/omaha_request_params.h
+++ b/omaha_request_params.h
@@ -19,138 +19,221 @@
 // The default "official" Omaha update URL.
 extern const char* const kProductionOmahaUrl;
 
-// This struct encapsulates the data Omaha gets for the request, along with
-// essential state needed for the processing of the request/response.
-// The strings in this struct should not be XML escaped.
-// TODO (jaysri): Consider renaming this to reflect its lifetime more
-// appropriately.
-struct OmahaRequestParams {
+class SystemState;
 
-  OmahaRequestParams()
-      : os_platform(kOsPlatform),
-        os_version(kOsVersion),
-        app_id(kAppId),
-        delta_okay(true),
-        interactive(false),
-        update_disabled(false),
-        wall_clock_based_wait_enabled(false),
-        update_check_count_wait_enabled(false),
-        min_update_checks_needed(kDefaultMinUpdateChecks),
-        max_update_checks_allowed(kDefaultMaxUpdateChecks) {}
+// This class encapsulates the data Omaha gets for the request, along with
+// essential state needed for the processing of the request/response.  The
+// strings in this struct should not be XML escaped.
+//
+// TODO (jaysri): chromium-os:39752 tracks the need to rename this class to
+// reflect its lifetime more appropriately.
+class OmahaRequestParams {
+ public:
+  OmahaRequestParams(SystemState* system_state)
+      : system_state_(system_state),
+        os_platform_(kOsPlatform),
+        os_version_(kOsVersion),
+        app_id_(kAppId),
+        board_app_id_(kAppId),
+        delta_okay_(true),
+        interactive_(false),
+        update_disabled_(false),
+        wall_clock_based_wait_enabled_(false),
+        update_check_count_wait_enabled_(false),
+        min_update_checks_needed_(kDefaultMinUpdateChecks),
+        max_update_checks_allowed_(kDefaultMaxUpdateChecks),
+        is_powerwash_allowed_(false),
+        force_lock_down_(false),
+        forced_lock_down_(false) {
+    InitFromLsbValue();
+  }
 
-  OmahaRequestParams(const std::string& in_os_platform,
+  OmahaRequestParams(SystemState* system_state,
+                     const std::string& in_os_platform,
                      const std::string& in_os_version,
                      const std::string& in_os_sp,
                      const std::string& in_os_board,
                      const std::string& in_app_id,
                      const std::string& in_app_version,
                      const std::string& in_app_lang,
-                     const std::string& in_app_track,
-                     const std::string& in_hardware_class,
+                     const std::string& in_target_channel,
+                     const std::string& in_hwid,
                      bool in_delta_okay,
                      bool in_interactive,
                      const std::string& in_update_url,
                      bool in_update_disabled,
                      const std::string& in_target_version_prefix)
-      : os_platform(in_os_platform),
-        os_version(in_os_version),
-        os_sp(in_os_sp),
-        os_board(in_os_board),
-        app_id(in_app_id),
-        app_version(in_app_version),
-        app_lang(in_app_lang),
-        app_track(in_app_track),
-        hardware_class(in_hardware_class),
-        delta_okay(in_delta_okay),
-        interactive(in_interactive),
-        update_url(in_update_url),
-        update_disabled(in_update_disabled),
-        target_version_prefix(in_target_version_prefix),
-        wall_clock_based_wait_enabled(false),
-        update_check_count_wait_enabled(false),
-        min_update_checks_needed(kDefaultMinUpdateChecks),
-        max_update_checks_allowed(kDefaultMaxUpdateChecks) {}
+      : system_state_(system_state),
+        os_platform_(in_os_platform),
+        os_version_(in_os_version),
+        os_sp_(in_os_sp),
+        os_board_(in_os_board),
+        app_id_(in_app_id),
+        board_app_id_(in_app_id),
+        app_version_(in_app_version),
+        app_lang_(in_app_lang),
+        current_channel_(in_target_channel),
+        target_channel_(in_target_channel),
+        hwid_(in_hwid),
+        delta_okay_(in_delta_okay),
+        interactive_(in_interactive),
+        update_url_(in_update_url),
+        update_disabled_(in_update_disabled),
+        target_version_prefix_(in_target_version_prefix),
+        wall_clock_based_wait_enabled_(false),
+        update_check_count_wait_enabled_(false),
+        min_update_checks_needed_(kDefaultMinUpdateChecks),
+        max_update_checks_allowed_(kDefaultMaxUpdateChecks),
+        is_powerwash_allowed_(false),
+        force_lock_down_(false),
+        forced_lock_down_(false) {}
 
-  std::string os_platform;
-  std::string os_version;
-  std::string os_sp;
-  std::string os_board;
-  std::string app_id;
-  std::string app_version;
-  std::string app_lang;
-  std::string app_track;
-  std::string hardware_class;  // Hardware Qualification ID of the client
-  bool delta_okay;  // If this client can accept a delta
-  bool interactive;   // Whether this is a user-initiated update check
+  // Setters and getters for the various properties.
+  inline std::string os_platform() const { return os_platform_; }
+  inline std::string os_version() const { return os_version_; }
+  inline std::string os_sp() const { return os_sp_; }
+  inline std::string os_board() const { return os_board_; }
+  inline std::string app_id() const { return app_id_; }
+  inline std::string board_app_id() const { return board_app_id_; }
+  inline std::string app_lang() const { return app_lang_; }
+  inline std::string hwid() const { return hwid_; }
 
-  std::string update_url;
+  inline void set_app_version(const std::string& version) {
+    app_version_ = version;
+  }
+  inline std::string app_version() const { return app_version_; }
 
-  static const char kUpdateTrackKey[];
+  inline std::string current_channel() const { return current_channel_; }
+  inline std::string target_channel() const { return target_channel_; }
 
-  bool update_disabled;
-  std::string target_version_prefix;
+  // Can client accept a delta ?
+  inline void set_delta_okay(bool ok) { delta_okay_ = ok; }
+  inline bool delta_okay() const { return delta_okay_; }
 
-  bool wall_clock_based_wait_enabled;
-  base::TimeDelta waiting_period;
+  // True if this is a user-initiated update check.
+  inline bool interactive() const { return interactive_; }
 
-  bool update_check_count_wait_enabled;
-  int64 min_update_checks_needed;
-  int64 max_update_checks_allowed;
+  inline void set_update_url(const std::string& url) { update_url_ = url; }
+  inline std::string update_url() const { return update_url_; }
+
+  inline void set_update_disabled(bool disabled) {
+    update_disabled_ = disabled;
+  }
+  inline bool update_disabled() const { return update_disabled_; }
+
+  inline void set_target_version_prefix(const std::string& prefix) {
+    target_version_prefix_ = prefix;
+  }
+
+  inline std::string target_version_prefix() const {
+    return target_version_prefix_;
+  }
+
+  inline void set_wall_clock_based_wait_enabled(bool enabled) {
+    wall_clock_based_wait_enabled_ = enabled;
+  }
+  inline bool wall_clock_based_wait_enabled() const {
+    return wall_clock_based_wait_enabled_;
+  }
+
+  inline void set_waiting_period(base::TimeDelta period) {
+    waiting_period_ = period;
+  }
+  base::TimeDelta waiting_period() const { return waiting_period_; }
+
+  inline void set_update_check_count_wait_enabled(bool enabled) {
+    update_check_count_wait_enabled_ = enabled;
+  }
+
+  inline bool update_check_count_wait_enabled() const {
+    return update_check_count_wait_enabled_;
+  }
+
+  inline void set_min_update_checks_needed(int64 min) {
+    min_update_checks_needed_ = min;
+  }
+  inline int64 min_update_checks_needed() const {
+    return min_update_checks_needed_;
+  }
+
+  inline void set_max_update_checks_allowed(int64 max) {
+    max_update_checks_allowed_ = max;
+  }
+  inline int64 max_update_checks_allowed() const {
+    return max_update_checks_allowed_;
+  }
+
+  // True if we're trying to update to a more stable channel.
+  // i.e. index(target_channel) > index(current_channel).
+  bool to_more_stable_channel() const;
 
   // Suggested defaults
   static const char* const kAppId;
   static const char* const kOsPlatform;
   static const char* const kOsVersion;
   static const char* const kUpdateUrl;
+  static const char* const kUpdateChannelKey;
+  static const char* const kIsPowerwashAllowedKey;
   static const int64 kDefaultMinUpdateChecks = 0;
   static const int64 kDefaultMaxUpdateChecks = 8;
-};
-
-class OmahaRequestDeviceParams : public OmahaRequestParams {
- public:
-  OmahaRequestDeviceParams();
 
   // Initializes all the data in the object. Non-empty
   // |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_release_track,
             bool in_interactive);
 
-  // Permanently changes the release track to |track|. Returns true on success,
-  // false otherwise.
-  bool SetTrack(const std::string& track);
-  static bool SetDeviceTrack(const std::string& track);
+  // Permanently changes the release channel to |channel|. Performs a
+  // powerwash, if required and allowed.
+  // Returns true on success, false otherwise. Note: This call will fail if
+  // there's a channel change pending already. This is to serialize all the
+  // channel changes done by the user in order to avoid having to solve
+  // numerous edge cases around ensuring the powerwash happens as intended in
+  // all such cases.
+  bool SetTargetChannel(const std::string& channel, bool is_powerwash_allowed);
 
-  // Returns the release track. On error, returns an empty string.
-  static std::string GetDeviceTrack();
+  bool is_powerwash_allowed() const { return is_powerwash_allowed_; }
 
   // For unit-tests.
-  void set_root(const std::string& root) { root_ = root; }
+  void set_root(const std::string& root);
 
   // Enforce security mode for testing purposes.
   void SetLockDown(bool lock);
 
  private:
-  FRIEND_TEST(OmahaRequestDeviceParamsTest, IsValidTrackTest);
-  FRIEND_TEST(OmahaRequestDeviceParamsTest, ShouldLockDownTest);
+  FRIEND_TEST(OmahaRequestParamsTest, IsValidChannelTest);
+  FRIEND_TEST(OmahaRequestParamsTest, ShouldLockDownTest);
+  FRIEND_TEST(OmahaRequestParamsTest, ChannelIndexTest);
+  FRIEND_TEST(OmahaRequestParamsTest, LsbPreserveTest);
 
   // Use a validator that is a non-static member of this class so that its
-  // inputs can be mocked in unit tests (e.g., build type for IsValidTrack).
-  typedef bool(OmahaRequestDeviceParams::*ValueValidator)(
+  // inputs can be mocked in unit tests (e.g., build type for IsValidChannel).
+  typedef bool(OmahaRequestParams::*ValueValidator)(
       const std::string&) const;
 
   // Returns true if parameter values should be locked down for security
   // reasons. If this is an official build running in normal boot mode, all
-  // values except the release track are parsed only from the read-only rootfs
-  // partition and the track values are restricted to a pre-approved set.
+  // values except the release channel are parsed only from the read-only rootfs
+  // partition and the channel values are restricted to a pre-approved set.
   bool ShouldLockDown() const;
 
-  // Returns true if |track| is a valid track, false otherwise. This method
-  // restricts the track value only if the image is official (see
+  // Returns true if |channel| is a valid channel, false otherwise. This method
+  // restricts the channel value only if the image is official (see
   // IsOfficialBuild).
-  bool IsValidTrack(const std::string& track) const;
+  bool IsValidChannel(const std::string& channel) const;
+
+  // Returns the index of the given channel.
+  int GetChannelIndex(const std::string& channel) const;
+
+  // These are individual helper methods to initialize the said properties from
+  // the LSB value.
+  void SetTargetChannelFromLsbValue();
+  void SetCurrentChannelFromLsbValue();
+  void SetIsPowerwashAllowedFromLsbValue();
+
+  // Initializes the required properties from the LSB value.
+  void InitFromLsbValue();
 
   // Fetches the value for a given key from
   // /mnt/stateful_partition/etc/lsb-release if possible and |stateful_override|
@@ -164,6 +247,60 @@
   // Gets the machine type (e.g. "i686").
   std::string GetMachineType() const;
 
+  // Global system context.
+  SystemState* system_state_;
+
+  // Basic properties of the OS and Application that go into the Omaha request.
+  std::string os_platform_;
+  std::string os_version_;
+  std::string os_sp_;
+  std::string os_board_;
+
+  // The app_id identifies the board except when we're on canary-channel.
+  // Whereas the board_app_id always identifies the board irrespective of the
+  // channel we are on. They are required the facilitate the switching from
+  // canary to a non-canary channel.
+  std::string app_id_;
+  std::string board_app_id_;
+
+  std::string app_version_;
+  std::string app_lang_;
+
+  // Current channel and target channel. Usually there are same, except when
+  // there's a pending channel change.
+  std::string current_channel_;
+  std::string target_channel_;
+  std::string hwid_;  // Hardware Qualification ID of the client
+  bool delta_okay_;  // If this client can accept a delta
+  bool interactive_;   // Whether this is a user-initiated update check
+
+  // The URL to send the Omaha request to.
+  std::string update_url_;
+
+  // True if we've been told to block updates per enterprise policy.
+  bool update_disabled_;
+
+  // Prefix of the target OS version that the enterprise wants this device
+  // to be pinned to. It's empty otherwise.
+  std::string target_version_prefix_;
+
+  // True if scattering is enabled, in which case waiting_period_ specifies the
+  // amount of absolute time that we've to wait for before sending a request to
+  // Omaha.
+  bool wall_clock_based_wait_enabled_;
+  base::TimeDelta waiting_period_;
+
+  // True if scattering is enabled to denote the number of update checks
+  // we've to skip before we can send a request to Omaha. The min and max
+  // values establish the bounds for a random number to be chosen within that
+  // range to enable such a wait.
+  bool update_check_count_wait_enabled_;
+  int64 min_update_checks_needed_;
+  int64 max_update_checks_allowed_;
+
+  // True if we are allowed to do powerwash, if required, on a channel change.
+  bool is_powerwash_allowed_;
+
   // When reading files, prepend root_ to the paths. Useful for testing.
   std::string root_;
 
@@ -171,7 +308,9 @@
   bool force_lock_down_;
   bool forced_lock_down_;
 
-  DISALLOW_COPY_AND_ASSIGN(OmahaRequestDeviceParams);
+  // TODO(jaysri): Uncomment this after fixing unit tests, as part of
+  // chromium-os:39752
+  // DISALLOW_COPY_AND_ASSIGN(OmahaRequestParams);
 };
 
 }  // namespace chromeos_update_engine
diff --git a/omaha_request_params_unittest.cc b/omaha_request_params_unittest.cc
index f3a1832..3c76cd2 100644
--- a/omaha_request_params_unittest.cc
+++ b/omaha_request_params_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/file_util.h"
 #include "gtest/gtest.h"
 #include "update_engine/install_plan.h"
+#include "update_engine/mock_system_state.h"
 #include "update_engine/omaha_request_params.h"
 #include "update_engine/test_utils.h"
 #include "update_engine/utils.h"
@@ -17,9 +18,12 @@
 
 namespace chromeos_update_engine {
 
-class OmahaRequestDeviceParamsTest : public ::testing::Test {
+class OmahaRequestParamsTest : public ::testing::Test {
+ public:
+  OmahaRequestParamsTest() : params_(NULL) {}
+
  protected:
-  // Return true iff the OmahaRequestDeviceParams::Init succeeded. If
+  // Return true iff the OmahaRequestParams::Init succeeded. If
   // out is non-NULL, it's set w/ the generated data.
   bool DoTest(OmahaRequestParams* out, const string& app_version,
               const string& omaha_url);
@@ -28,6 +32,11 @@
     ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir + "/etc"));
     ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir +
                         utils::kStatefulPartition + "/etc"));
+    // Create a fresh copy of the params for each test, so there's no
+    // unintended reuse of state across tests.
+    MockSystemState mock_system_state;
+    OmahaRequestParams new_params(&mock_system_state);
+    params_ = new_params;
     params_.set_root(string("./") + kTestDir);
     params_.SetLockDown(false);
   }
@@ -36,18 +45,18 @@
     EXPECT_EQ(0, System(string("rm -rf ") + kTestDir));
   }
 
-  OmahaRequestDeviceParams params_;
+  OmahaRequestParams params_;
 
   static const string kTestDir;
 };
 
-const string OmahaRequestDeviceParamsTest::kTestDir =
-    "omaha_request_device_params-test";
+const string OmahaRequestParamsTest::kTestDir =
+    "omaha_request_params-test";
 
-bool OmahaRequestDeviceParamsTest::DoTest(OmahaRequestParams* out,
-                                          const string& app_version,
-                                          const string& omaha_url) {
-  bool success = params_.Init(app_version, omaha_url, "", false);
+bool OmahaRequestParamsTest::DoTest(OmahaRequestParams* out,
+                                    const string& app_version,
+                                    const string& omaha_url) {
+  bool success = params_.Init(app_version, omaha_url, false);
   if (out)
     *out = params_;
   return success;
@@ -66,333 +75,344 @@
 }
 }  // namespace {}
 
-TEST_F(OmahaRequestDeviceParamsTest, SimpleTest) {
+TEST_F(OmahaRequestParamsTest, SimpleTest) {
   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=footrack\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel\n"
       "CHROMEOS_AUSERVER=http://www.google.com"));
-  OmahaRequestParams out;
+  MockSystemState mock_system_state;
+  OmahaRequestParams out(&mock_system_state);
   EXPECT_TRUE(DoTest(&out, "", ""));
-  EXPECT_EQ("Chrome OS", out.os_platform);
-  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp);
-  EXPECT_EQ("arm-generic", out.os_board);
-  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id);
-  EXPECT_EQ("0.2.2.3", out.app_version);
-  EXPECT_EQ("en-US", out.app_lang);
-  EXPECT_EQ("", out.hardware_class);
-  EXPECT_TRUE(out.delta_okay);
-  EXPECT_EQ("footrack", out.app_track);
-  EXPECT_EQ("http://www.google.com", out.update_url);
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("arm-generic", out.os_board());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_EQ("", out.hwid());
+  EXPECT_TRUE(out.delta_okay());
+  EXPECT_EQ("dev-channel", out.target_channel());
+  EXPECT_EQ("http://www.google.com", out.update_url());
 }
 
-TEST_F(OmahaRequestDeviceParamsTest, AppIDTest) {
+TEST_F(OmahaRequestParamsTest, AppIDTest) {
   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=footrack\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel\n"
       "CHROMEOS_RELEASE_APPID={58c35cef-9d30-476e-9098-ce20377d535d}\n"
       "CHROMEOS_AUSERVER=http://www.google.com"));
-  OmahaRequestParams out;
+  MockSystemState mock_system_state;
+  OmahaRequestParams out(&mock_system_state);
   EXPECT_TRUE(DoTest(&out, "", ""));
-  EXPECT_EQ("Chrome OS", out.os_platform);
-  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp);
-  EXPECT_EQ("arm-generic", out.os_board);
-  EXPECT_EQ("{58c35cef-9d30-476e-9098-ce20377d535d}", out.app_id);
-  EXPECT_EQ("0.2.2.3", out.app_version);
-  EXPECT_EQ("en-US", out.app_lang);
-  EXPECT_EQ("", out.hardware_class);
-  EXPECT_TRUE(out.delta_okay);
-  EXPECT_EQ("footrack", out.app_track);
-  EXPECT_EQ("http://www.google.com", out.update_url);
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("arm-generic", out.os_board());
+  EXPECT_EQ("{58c35cef-9d30-476e-9098-ce20377d535d}", out.app_id());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_EQ("", out.hwid());
+  EXPECT_TRUE(out.delta_okay());
+  EXPECT_EQ("dev-channel", out.target_channel());
+  EXPECT_EQ("http://www.google.com", out.update_url());
 }
 
-TEST_F(OmahaRequestDeviceParamsTest, MissingTrackTest) {
+TEST_F(OmahaRequestParamsTest, MissingChannelTest) {
   ASSERT_TRUE(WriteFileString(
       kTestDir + "/etc/lsb-release",
       "CHROMEOS_RELEASE_FOO=bar\n"
       "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
-      "CHROMEOS_RELEASE_TRXCK=footrack"));
-  OmahaRequestParams out;
+      "CHROMEOS_RELEASE_TRXCK=dev-channel"));
+  MockSystemState mock_system_state;
+  OmahaRequestParams out(&mock_system_state);
   EXPECT_TRUE(DoTest(&out, "", ""));
-  EXPECT_EQ("Chrome OS", out.os_platform);
-  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp);
-  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id);
-  EXPECT_EQ("0.2.2.3", out.app_version);
-  EXPECT_EQ("en-US", out.app_lang);
-  EXPECT_EQ("", out.app_track);
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_EQ("", out.target_channel());
 }
 
-TEST_F(OmahaRequestDeviceParamsTest, ConfusingReleaseTest) {
+TEST_F(OmahaRequestParamsTest, ConfusingReleaseTest) {
   ASSERT_TRUE(WriteFileString(
       kTestDir + "/etc/lsb-release",
       "CHROMEOS_RELEASE_FOO=CHROMEOS_RELEASE_VERSION=1.2.3.4\n"
       "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
-      "CHROMEOS_RELEASE_TRXCK=footrack"));
-  OmahaRequestParams out;
+      "CHROMEOS_RELEASE_TRXCK=dev-channel"));
+  MockSystemState mock_system_state;
+  OmahaRequestParams out(&mock_system_state);
   EXPECT_TRUE(DoTest(&out, "", ""));
-  EXPECT_EQ("Chrome OS", out.os_platform);
-  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp);
-  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id);
-  EXPECT_EQ("0.2.2.3", out.app_version);
-  EXPECT_EQ("en-US", out.app_lang);
-  EXPECT_EQ("", out.app_track);
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_EQ("", out.target_channel());
 }
 
-TEST_F(OmahaRequestDeviceParamsTest, MissingVersionTest) {
+TEST_F(OmahaRequestParamsTest, MissingVersionTest) {
   ASSERT_TRUE(WriteFileString(
       kTestDir + "/etc/lsb-release",
       "CHROMEOS_RELEASE_BOARD=arm-generic\n"
       "CHROMEOS_RELEASE_FOO=bar\n"
-      "CHROMEOS_RELEASE_TRACK=footrack"));
-  OmahaRequestParams out;
+      "CHROMEOS_RELEASE_TRACK=dev-channel"));
+  MockSystemState mock_system_state;
+  OmahaRequestParams out(&mock_system_state);
   EXPECT_TRUE(DoTest(&out, "", ""));
-  EXPECT_EQ("Chrome OS", out.os_platform);
-  EXPECT_EQ(string("_") + GetMachineType(), out.os_sp);
-  EXPECT_EQ("arm-generic", out.os_board);
-  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id);
-  EXPECT_EQ("", out.app_version);
-  EXPECT_EQ("en-US", out.app_lang);
-  EXPECT_TRUE(out.delta_okay);
-  EXPECT_EQ("footrack", out.app_track);
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("arm-generic", out.os_board());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id());
+  EXPECT_EQ("", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_TRUE(out.delta_okay());
+  EXPECT_EQ("dev-channel", out.target_channel());
 }
 
-TEST_F(OmahaRequestDeviceParamsTest, ForceVersionTest) {
+TEST_F(OmahaRequestParamsTest, ForceVersionTest) {
   ASSERT_TRUE(WriteFileString(
       kTestDir + "/etc/lsb-release",
       "CHROMEOS_RELEASE_BOARD=arm-generic\n"
       "CHROMEOS_RELEASE_FOO=bar\n"
-      "CHROMEOS_RELEASE_TRACK=footrack"));
-  OmahaRequestParams out;
+      "CHROMEOS_RELEASE_TRACK=dev-channel"));
+  MockSystemState mock_system_state;
+  OmahaRequestParams out(&mock_system_state);
   EXPECT_TRUE(DoTest(&out, "ForcedVersion", ""));
-  EXPECT_EQ("Chrome OS", out.os_platform);
-  EXPECT_EQ(string("ForcedVersion_") + GetMachineType(), out.os_sp);
-  EXPECT_EQ("arm-generic", out.os_board);
-  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id);
-  EXPECT_EQ("ForcedVersion", out.app_version);
-  EXPECT_EQ("en-US", out.app_lang);
-  EXPECT_TRUE(out.delta_okay);
-  EXPECT_EQ("footrack", out.app_track);
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("ForcedVersion_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("arm-generic", out.os_board());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id());
+  EXPECT_EQ("ForcedVersion", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_TRUE(out.delta_okay());
+  EXPECT_EQ("dev-channel", out.target_channel());
 }
 
-TEST_F(OmahaRequestDeviceParamsTest, ForcedURLTest) {
+TEST_F(OmahaRequestParamsTest, ForcedURLTest) {
   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=footrack"));
-  OmahaRequestParams out;
+      "CHROMEOS_RELEASE_TRACK=dev-channel"));
+  MockSystemState mock_system_state;
+  OmahaRequestParams out(&mock_system_state);
   EXPECT_TRUE(DoTest(&out, "", "http://forced.google.com"));
-  EXPECT_EQ("Chrome OS", out.os_platform);
-  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp);
-  EXPECT_EQ("arm-generic", out.os_board);
-  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id);
-  EXPECT_EQ("0.2.2.3", out.app_version);
-  EXPECT_EQ("en-US", out.app_lang);
-  EXPECT_TRUE(out.delta_okay);
-  EXPECT_EQ("footrack", out.app_track);
-  EXPECT_EQ("http://forced.google.com", out.update_url);
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("arm-generic", out.os_board());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_TRUE(out.delta_okay());
+  EXPECT_EQ("dev-channel", out.target_channel());
+  EXPECT_EQ("http://forced.google.com", out.update_url());
 }
 
-TEST_F(OmahaRequestDeviceParamsTest, MissingURLTest) {
+TEST_F(OmahaRequestParamsTest, MissingURLTest) {
   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=footrack"));
-  OmahaRequestParams out;
+      "CHROMEOS_RELEASE_TRACK=dev-channel"));
+  MockSystemState mock_system_state;
+  OmahaRequestParams out(&mock_system_state);
   EXPECT_TRUE(DoTest(&out, "", ""));
-  EXPECT_EQ("Chrome OS", out.os_platform);
-  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp);
-  EXPECT_EQ("arm-generic", out.os_board);
-  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id);
-  EXPECT_EQ("0.2.2.3", out.app_version);
-  EXPECT_EQ("en-US", out.app_lang);
-  EXPECT_TRUE(out.delta_okay);
-  EXPECT_EQ("footrack", out.app_track);
-  EXPECT_EQ(kProductionOmahaUrl, out.update_url);
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("arm-generic", out.os_board());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_TRUE(out.delta_okay());
+  EXPECT_EQ("dev-channel", out.target_channel());
+  EXPECT_EQ(kProductionOmahaUrl, out.update_url());
 }
 
-TEST_F(OmahaRequestDeviceParamsTest, NoDeltasTest) {
+TEST_F(OmahaRequestParamsTest, NoDeltasTest) {
   ASSERT_TRUE(WriteFileString(
       kTestDir + "/etc/lsb-release",
       "CHROMEOS_RELEASE_FOO=CHROMEOS_RELEASE_VERSION=1.2.3.4\n"
       "CHROMEOS_RELEASE_VERSION=0.2.2.3\n"
-      "CHROMEOS_RELEASE_TRXCK=footrack"));
+      "CHROMEOS_RELEASE_TRXCK=dev-channel"));
   ASSERT_TRUE(WriteFileString(kTestDir + "/.nodelta", ""));
-  OmahaRequestParams out;
+  MockSystemState mock_system_state;
+  OmahaRequestParams out(&mock_system_state);
   EXPECT_TRUE(DoTest(&out, "", ""));
-  EXPECT_FALSE(out.delta_okay);
+  EXPECT_FALSE(out.delta_okay());
 }
 
-TEST_F(OmahaRequestDeviceParamsTest, OverrideTest) {
+TEST_F(OmahaRequestParamsTest, OverrideTest) {
   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=footrack\n"
-      "CHROMEOS_AUSERVER=http://www.google.com"));
-  ASSERT_TRUE(WriteFileString(
-      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
-      "CHROMEOS_RELEASE_BOARD=x86-generic\n"
-      "CHROMEOS_RELEASE_TRACK=bartrack\n"
-      "CHROMEOS_AUSERVER=https://www.google.com"));
-  OmahaRequestParams out;
-  EXPECT_TRUE(DoTest(&out, "", ""));
-  EXPECT_EQ("Chrome OS", out.os_platform);
-  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp);
-  EXPECT_EQ("x86-generic", out.os_board);
-  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id);
-  EXPECT_EQ("0.2.2.3", out.app_version);
-  EXPECT_EQ("en-US", out.app_lang);
-  EXPECT_EQ("", out.hardware_class);
-  EXPECT_FALSE(out.delta_okay);
-  EXPECT_EQ("bartrack", out.app_track);
-  EXPECT_EQ("https://www.google.com", out.update_url);
-}
-
-TEST_F(OmahaRequestDeviceParamsTest, OverrideLockDownTest) {
-  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=footrack\n"
-      "CHROMEOS_AUSERVER=https://www.google.com"));
-  ASSERT_TRUE(WriteFileString(
-      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
-      "CHROMEOS_RELEASE_BOARD=x86-generic\n"
       "CHROMEOS_RELEASE_TRACK=dev-channel\n"
       "CHROMEOS_AUSERVER=http://www.google.com"));
+  ASSERT_TRUE(WriteFileString(
+      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=x86-generic\n"
+      "CHROMEOS_RELEASE_TRACK=beta-channel\n"
+      "CHROMEOS_AUSERVER=https://www.google.com"));
+  MockSystemState mock_system_state;
+  OmahaRequestParams out(&mock_system_state);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("x86-generic", out.os_board());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_EQ("", out.hwid());
+  EXPECT_FALSE(out.delta_okay());
+  EXPECT_EQ("beta-channel", out.target_channel());
+  EXPECT_EQ("https://www.google.com", out.update_url());
+}
+
+TEST_F(OmahaRequestParamsTest, OverrideLockDownTest) {
+  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=https://www.google.com"));
+  ASSERT_TRUE(WriteFileString(
+      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=x86-generic\n"
+      "CHROMEOS_RELEASE_TRACK=stable-channel\n"
+      "CHROMEOS_AUSERVER=http://www.google.com"));
   params_.SetLockDown(true);
-  OmahaRequestParams out;
+  MockSystemState mock_system_state;
+  OmahaRequestParams out(&mock_system_state);
   EXPECT_TRUE(DoTest(&out, "", ""));
-  EXPECT_EQ("arm-generic", out.os_board);
-  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id);
-  EXPECT_EQ("0.2.2.3", out.app_version);
-  EXPECT_EQ("", out.hardware_class);
-  EXPECT_FALSE(out.delta_okay);
-  EXPECT_EQ("dev-channel", out.app_track);
-  EXPECT_EQ("https://www.google.com", out.update_url);
+  EXPECT_EQ("arm-generic", out.os_board());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ("", out.hwid());
+  EXPECT_FALSE(out.delta_okay());
+  EXPECT_EQ("stable-channel", out.target_channel());
+  EXPECT_EQ("https://www.google.com", out.update_url());
 }
 
-TEST_F(OmahaRequestDeviceParamsTest, OverrideSameTrackTest) {
+TEST_F(OmahaRequestParamsTest, OverrideSameChannelTest) {
   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=footrack\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel\n"
       "CHROMEOS_AUSERVER=http://www.google.com"));
   ASSERT_TRUE(WriteFileString(
       kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
       "CHROMEOS_RELEASE_BOARD=x86-generic\n"
-      "CHROMEOS_RELEASE_TRACK=footrack"));
-  OmahaRequestParams out;
+      "CHROMEOS_RELEASE_TRACK=dev-channel"));
+  MockSystemState mock_system_state;
+  OmahaRequestParams out(&mock_system_state);
   EXPECT_TRUE(DoTest(&out, "", ""));
-  EXPECT_EQ("x86-generic", out.os_board);
-  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id);
-  EXPECT_EQ("0.2.2.3", out.app_version);
-  EXPECT_EQ("", out.hardware_class);
-  EXPECT_TRUE(out.delta_okay);
-  EXPECT_EQ("footrack", out.app_track);
-  EXPECT_EQ("http://www.google.com", out.update_url);
+  EXPECT_EQ("x86-generic", out.os_board());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ("", out.hwid());
+  EXPECT_TRUE(out.delta_okay());
+  EXPECT_EQ("dev-channel", out.target_channel());
+  EXPECT_EQ("http://www.google.com", out.update_url());
 }
 
-TEST_F(OmahaRequestDeviceParamsTest, SetTrackSimpleTest) {
+TEST_F(OmahaRequestParamsTest, SetTargetChannelTest) {
   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=footrack\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel\n"
       "CHROMEOS_AUSERVER=http://www.google.com"));
   {
-    OmahaRequestDeviceParams params;
+    MockSystemState mock_system_state;
+    OmahaRequestParams params(&mock_system_state);
     params.set_root(string("./") + kTestDir);
     params.SetLockDown(false);
-    EXPECT_TRUE(params.Init("", "", "", false));
-    params.SetTrack("zootrack");
+    EXPECT_TRUE(params.Init("", "", false));
+    params.SetTargetChannel("canary-channel", false);
+    EXPECT_FALSE(params.is_powerwash_allowed());
   }
-  OmahaRequestParams out;
+  MockSystemState mock_system_state;
+  OmahaRequestParams out(&mock_system_state);
   EXPECT_TRUE(DoTest(&out, "", ""));
-  EXPECT_EQ("arm-generic", out.os_board);
-  EXPECT_EQ("zootrack", out.app_track);
+  EXPECT_EQ("canary-channel", out.target_channel());
+  EXPECT_FALSE(out.is_powerwash_allowed());
 }
 
-TEST_F(OmahaRequestDeviceParamsTest, SetTrackPreserveTest) {
+TEST_F(OmahaRequestParamsTest, SetIsPowerwashAllowedTest) {
   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=footrack\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel\n"
       "CHROMEOS_AUSERVER=http://www.google.com"));
-  ASSERT_TRUE(WriteFileString(
-      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
-      "CHROMEOS_RELEASE_BOARD=x86-generic\n"
-      "CHROMEOS_RELEASE_TRACK=bartrack"));
   {
-    OmahaRequestDeviceParams params;
+    MockSystemState mock_system_state;
+    OmahaRequestParams params(&mock_system_state);
     params.set_root(string("./") + kTestDir);
     params.SetLockDown(false);
-    EXPECT_TRUE(params.Init("", "", "", false));
-    params.SetTrack("zootrack");
+    EXPECT_TRUE(params.Init("", "", false));
+    params.SetTargetChannel("canary-channel", true);
+    EXPECT_TRUE(params.is_powerwash_allowed());
   }
-  OmahaRequestParams out;
+  MockSystemState mock_system_state;
+  OmahaRequestParams out(&mock_system_state);
   EXPECT_TRUE(DoTest(&out, "", ""));
-  EXPECT_EQ("x86-generic", out.os_board);
-  EXPECT_EQ("zootrack", out.app_track);
+  EXPECT_EQ("canary-channel", out.target_channel());
+  EXPECT_TRUE(out.is_powerwash_allowed());
 }
 
-TEST_F(OmahaRequestDeviceParamsTest, SetTrackInvalidTest) {
+TEST_F(OmahaRequestParamsTest, SetTargetChannelInvalidTest) {
   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=footrack\n"
+      "CHROMEOS_RELEASE_TRACK=dev-channel\n"
       "CHROMEOS_AUSERVER=http://www.google.com"));
   {
-    OmahaRequestDeviceParams params;
+    MockSystemState mock_system_state;
+    OmahaRequestParams params(&mock_system_state);
     params.set_root(string("./") + kTestDir);
     params.SetLockDown(true);
-    EXPECT_TRUE(params.Init("", "", "", false));
-    params.SetTrack("zootrack");
+    EXPECT_TRUE(params.Init("", "", false));
+    params.SetTargetChannel("dogfood-channel", true);
+    EXPECT_FALSE(params.is_powerwash_allowed());
   }
-  OmahaRequestParams out;
+  MockSystemState mock_system_state;
+  OmahaRequestParams out(&mock_system_state);
   EXPECT_TRUE(DoTest(&out, "", ""));
-  EXPECT_EQ("arm-generic", out.os_board);
-  EXPECT_EQ("footrack", out.app_track);
+  EXPECT_EQ("arm-generic", out.os_board());
+  EXPECT_EQ("dev-channel", out.target_channel());
+  EXPECT_FALSE(out.is_powerwash_allowed());
 }
 
-TEST_F(OmahaRequestDeviceParamsTest, IsValidTrackTest) {
-  params_.SetLockDown(true);
-  EXPECT_TRUE(params_.IsValidTrack("canary-channel"));
-  EXPECT_TRUE(params_.IsValidTrack("stable-channel"));
-  EXPECT_TRUE(params_.IsValidTrack("beta-channel"));
-  EXPECT_TRUE(params_.IsValidTrack("dev-channel"));
-  EXPECT_TRUE(params_.IsValidTrack("dogfood-channel"));
-  EXPECT_FALSE(params_.IsValidTrack("some-channel"));
-  EXPECT_FALSE(params_.IsValidTrack(""));
+TEST_F(OmahaRequestParamsTest, IsValidChannelTest) {
   params_.SetLockDown(false);
-  EXPECT_TRUE(params_.IsValidTrack("canary-channel"));
-  EXPECT_TRUE(params_.IsValidTrack("stable-channel"));
-  EXPECT_TRUE(params_.IsValidTrack("beta-channel"));
-  EXPECT_TRUE(params_.IsValidTrack("dev-channel"));
-  EXPECT_TRUE(params_.IsValidTrack("dogfood-channel"));
-  EXPECT_TRUE(params_.IsValidTrack("some-channel"));
-  EXPECT_TRUE(params_.IsValidTrack(""));
+  EXPECT_TRUE(params_.IsValidChannel("canary-channel"));
+  EXPECT_TRUE(params_.IsValidChannel("stable-channel"));
+  EXPECT_TRUE(params_.IsValidChannel("beta-channel"));
+  EXPECT_TRUE(params_.IsValidChannel("dev-channel"));
+  EXPECT_FALSE(params_.IsValidChannel("testimage-channel"));
+  EXPECT_FALSE(params_.IsValidChannel("dogfood-channel"));
+  EXPECT_FALSE(params_.IsValidChannel("some-channel"));
+  EXPECT_FALSE(params_.IsValidChannel(""));
 }
 
-TEST_F(OmahaRequestDeviceParamsTest, ValidTrackTest) {
+TEST_F(OmahaRequestParamsTest, ValidChannelTest) {
   ASSERT_TRUE(WriteFileString(
       kTestDir + "/etc/lsb-release",
       "CHROMEOS_RELEASE_BOARD=arm-generic\n"
@@ -401,21 +421,22 @@
       "CHROMEOS_RELEASE_TRACK=dev-channel\n"
       "CHROMEOS_AUSERVER=http://www.google.com"));
   params_.SetLockDown(true);
-  OmahaRequestParams out;
+  MockSystemState mock_system_state;
+  OmahaRequestParams out(&mock_system_state);
   EXPECT_TRUE(DoTest(&out, "", ""));
-  EXPECT_EQ("Chrome OS", out.os_platform);
-  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp);
-  EXPECT_EQ("arm-generic", out.os_board);
-  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id);
-  EXPECT_EQ("0.2.2.3", out.app_version);
-  EXPECT_EQ("en-US", out.app_lang);
-  EXPECT_EQ("", out.hardware_class);
-  EXPECT_TRUE(out.delta_okay);
-  EXPECT_EQ("dev-channel", out.app_track);
-  EXPECT_EQ("http://www.google.com", out.update_url);
+  EXPECT_EQ("Chrome OS", out.os_platform());
+  EXPECT_EQ(string("0.2.2.3_") + GetMachineType(), out.os_sp());
+  EXPECT_EQ("arm-generic", out.os_board());
+  EXPECT_EQ("{87efface-864d-49a5-9bb3-4b050a7c227a}", out.app_id());
+  EXPECT_EQ("0.2.2.3", out.app_version());
+  EXPECT_EQ("en-US", out.app_lang());
+  EXPECT_EQ("", out.hwid());
+  EXPECT_TRUE(out.delta_okay());
+  EXPECT_EQ("dev-channel", out.target_channel());
+  EXPECT_EQ("http://www.google.com", out.update_url());
 }
 
-TEST_F(OmahaRequestDeviceParamsTest, ChannelSpecified) {
+TEST_F(OmahaRequestParamsTest, SetTargetChannelWorks) {
   ASSERT_TRUE(WriteFileString(
       kTestDir + "/etc/lsb-release",
       "CHROMEOS_RELEASE_BOARD=arm-generic\n"
@@ -423,21 +444,74 @@
       "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", false);
-  EXPECT_EQ("beta-channel", params_.app_track);
+  params_.SetLockDown(false);
 
-  // When passed-in value is invalid, value from lsb-release should be used.
-  params_.Init("", "", "foo-channel", false);
-  EXPECT_EQ("dev-channel", params_.app_track);
+  // Check LSB value is used by default when SetTargetChannel is not called.
+  params_.Init("", "", false);
+  EXPECT_EQ("dev-channel", params_.target_channel());
 
-  // When passed-in value is empty, value from lsb-release should be used.
-  params_.Init("", "", "", false);
-  EXPECT_EQ("dev-channel", params_.app_track);
+  // When an invalid value is set, it should be ignored and the
+  // value from lsb-release should be used instead.
+  params_.Init("", "", false);
+  EXPECT_FALSE(params_.SetTargetChannel("invalid-channel", false));
+  EXPECT_EQ("dev-channel", params_.target_channel());
+
+  // When set to a valid value, it should take effect.
+  params_.Init("", "", false);
+  EXPECT_TRUE(params_.SetTargetChannel("beta-channel", true));
+  EXPECT_EQ("beta-channel", params_.target_channel());
+
+  // When set to the same value, it should be idempotent.
+  params_.Init("", "", false);
+  EXPECT_TRUE(params_.SetTargetChannel("beta-channel", true));
+  EXPECT_EQ("beta-channel", params_.target_channel());
+
+  // When set to a valid value while a change is already pending, it should
+  // fail.
+  params_.Init("", "", false);
+  EXPECT_FALSE(params_.SetTargetChannel("stable-channel", true));
+  EXPECT_EQ("beta-channel", params_.target_channel());
 }
 
-TEST_F(OmahaRequestDeviceParamsTest, ShouldLockDownTest) {
+TEST_F(OmahaRequestParamsTest, ChannelIndexTest) {
+  int canary = params_.GetChannelIndex("canary-channel");
+  int dev = params_.GetChannelIndex("dev-channel");
+  int beta = params_.GetChannelIndex("beta-channel");
+  int stable = params_.GetChannelIndex("stable-channel");
+  EXPECT_LE(canary, dev);
+  EXPECT_LE(dev, beta);
+  EXPECT_LE(beta, stable);
+
+  // testimage-channel or other names are not recognized, so index will be -1.
+  int testimage = params_.GetChannelIndex("testimage-channel");
+  int bogus = params_.GetChannelIndex("bogus-channel");
+  EXPECT_EQ(-1, testimage);
+  EXPECT_EQ(-1, bogus);
+}
+
+TEST_F(OmahaRequestParamsTest, ToMoreStableChannelFlagTest) {
+  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=canary-channel\n"
+      "CHROMEOS_AUSERVER=http://www.google.com"));
+  ASSERT_TRUE(WriteFileString(
+      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_BOARD=x86-generic\n"
+      "CHROMEOS_RELEASE_TRACK=stable-channel\n"
+      "CHROMEOS_AUSERVER=https://www.google.com"));
+  MockSystemState mock_system_state;
+  OmahaRequestParams out(&mock_system_state);
+  EXPECT_TRUE(DoTest(&out, "", ""));
+  EXPECT_EQ("https://www.google.com", out.update_url());
+  EXPECT_FALSE(out.delta_okay());
+  EXPECT_EQ("stable-channel", out.target_channel());
+  EXPECT_TRUE(out.to_more_stable_channel());
+}
+
+TEST_F(OmahaRequestParamsTest, ShouldLockDownTest) {
   EXPECT_FALSE(params_.ShouldLockDown());
 }
 
diff --git a/omaha_response_handler_action.cc b/omaha_response_handler_action.cc
index 24b8dcb..d705549 100644
--- a/omaha_response_handler_action.cc
+++ b/omaha_response_handler_action.cc
@@ -67,6 +67,11 @@
   install_plan_.kernel_install_path =
       utils::BootKernelDevice(install_plan_.install_path);
 
+  if (system_state_->request_params()->to_more_stable_channel() &&
+      system_state_->request_params()->is_powerwash_allowed()) {
+    install_plan_.powerwash_required = true;
+  }
+
   TEST_AND_RETURN(HasOutputPipe());
   if (HasOutputPipe())
     SetOutputObject(install_plan_);
diff --git a/omaha_response_handler_action_unittest.cc b/omaha_response_handler_action_unittest.cc
index c1c7e53..9f26bca 100644
--- a/omaha_response_handler_action_unittest.cc
+++ b/omaha_response_handler_action_unittest.cc
@@ -21,6 +21,10 @@
  public:
   // Return true iff the OmahaResponseHandlerAction succeeded.
   // If out is non-NULL, it's set w/ the response from the action.
+  bool DoTestCommon(MockSystemState* mock_system_state,
+                    const OmahaResponse& in,
+                    const string& boot_dev,
+                    InstallPlan* out);
   bool DoTest(const OmahaResponse& in,
               const string& boot_dev,
               InstallPlan* out);
@@ -56,22 +60,23 @@
     "-the_update_a.b.c.d_DELTA_.tgz";
 }  // namespace {}
 
-bool OmahaResponseHandlerActionTest::DoTest(const OmahaResponse& in,
-                                            const string& boot_dev,
-                                            InstallPlan* out) {
+bool OmahaResponseHandlerActionTest::DoTestCommon(
+    MockSystemState* mock_system_state,
+    const OmahaResponse& in,
+    const string& boot_dev,
+    InstallPlan* out) {
   ActionProcessor processor;
   OmahaResponseHandlerActionProcessorDelegate delegate;
   processor.set_delegate(&delegate);
 
   ObjectFeederAction<OmahaResponse> feeder_action;
   feeder_action.set_obj(in);
-  MockSystemState mock_system_state;
   if (in.update_exists) {
-    EXPECT_CALL(*mock_system_state.mock_prefs(),
+    EXPECT_CALL(*(mock_system_state->mock_prefs()),
                 SetString(kPrefsUpdateCheckResponseHash, in.hash))
         .WillOnce(Return(true));
   }
-  OmahaResponseHandlerAction response_handler_action(&mock_system_state);
+  OmahaResponseHandlerAction response_handler_action(mock_system_state);
   response_handler_action.set_boot_device(boot_dev);
   BondActions(&feeder_action, &response_handler_action);
   ObjectCollectorAction<InstallPlan> collector_action;
@@ -88,6 +93,13 @@
   return delegate.code_ == kActionCodeSuccess;
 }
 
+bool OmahaResponseHandlerActionTest::DoTest(const OmahaResponse& in,
+                                            const string& boot_dev,
+                                            InstallPlan* out) {
+  MockSystemState mock_system_state;
+  return DoTestCommon(&mock_system_state, in, boot_dev, out);
+}
+
 TEST_F(OmahaResponseHandlerActionTest, SimpleTest) {
   ScopedPathUnlinker deadline_unlinker(
       OmahaResponseHandlerAction::kDeadlineFile);
@@ -218,5 +230,78 @@
   EXPECT_TRUE(install_plan.hash_checks_mandatory);
 }
 
+TEST_F(OmahaResponseHandlerActionTest, ChangeToMoreStableChannelTest) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.display_version = "a.b.c.d";
+  in.payload_urls.push_back("https://MoreStableChannelTest");
+  in.more_info_url = "http://more/info";
+  in.hash = "HASHjk";
+  in.size = 15;
+
+  const string kTestDir = "omaha_response_handler_action-test";
+  ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir + "/etc"));
+  ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir +
+                        utils::kStatefulPartition + "/etc"));
+  ASSERT_TRUE(WriteFileString(
+      kTestDir + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_TRACK=canary-channel\n"));
+  ASSERT_TRUE(WriteFileString(
+      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
+      "CHROMEOS_IS_POWERWASH_ALLOWED=true\n"
+      "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
+
+  MockSystemState mock_system_state;
+  OmahaRequestParams params(&mock_system_state);
+  params.set_root(string("./") + kTestDir);
+  params.SetLockDown(false);
+  params.Init("1.2.3.4", "", 0);
+  EXPECT_EQ("canary-channel", params.current_channel());
+  EXPECT_EQ("stable-channel", params.target_channel());
+  EXPECT_TRUE(params.to_more_stable_channel());
+  EXPECT_TRUE(params.is_powerwash_allowed());
+
+  mock_system_state.set_request_params(&params);
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTestCommon(&mock_system_state, in, "/dev/sda5", &install_plan));
+  EXPECT_TRUE(install_plan.powerwash_required);
+}
+
+TEST_F(OmahaResponseHandlerActionTest, ChangeToLessStableChannelTest) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.display_version = "a.b.c.d";
+  in.payload_urls.push_back("https://LessStableChannelTest");
+  in.more_info_url = "http://more/info";
+  in.hash = "HASHjk";
+  in.size = 15;
+
+  const string kTestDir = "omaha_response_handler_action-test";
+  ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir + "/etc"));
+  ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir +
+                        utils::kStatefulPartition + "/etc"));
+  ASSERT_TRUE(WriteFileString(
+      kTestDir + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
+  ASSERT_TRUE(WriteFileString(
+      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
+      "CHROMEOS_RELEASE_TRACK=canary-channel\n"));
+
+  MockSystemState mock_system_state;
+  OmahaRequestParams params(&mock_system_state);
+  params.set_root(string("./") + kTestDir);
+  params.SetLockDown(false);
+  params.Init("5.6.7.8", "", 0);
+  EXPECT_EQ("stable-channel", params.current_channel());
+  params.SetTargetChannel("canary-channel", false);
+  EXPECT_EQ("canary-channel", params.target_channel());
+  EXPECT_FALSE(params.to_more_stable_channel());
+  EXPECT_FALSE(params.is_powerwash_allowed());
+
+  mock_system_state.set_request_params(&params);
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTestCommon(&mock_system_state, in, "/dev/sda5", &install_plan));
+  EXPECT_FALSE(install_plan.powerwash_required);
+}
 
 }  // namespace chromeos_update_engine
diff --git a/payload_state.cc b/payload_state.cc
index 9c4a7e4..1225947 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -171,6 +171,7 @@
     case kActionCodeOmahaUpdateIgnoredPerPolicy:
     case kActionCodeOmahaUpdateDeferredPerPolicy:
     case kActionCodeOmahaUpdateDeferredForBackoff:
+    case kActionCodePostinstallPowerwashError:
       LOG(INFO) << "Not incrementing URL index or failure count for this error";
       break;
 
diff --git a/payload_state_unittest.cc b/payload_state_unittest.cc
index 97bebd7..dfbbbaa 100644
--- a/payload_state_unittest.cc
+++ b/payload_state_unittest.cc
@@ -58,7 +58,7 @@
 class PayloadStateTest : public ::testing::Test { };
 
 TEST(PayloadStateTest, DidYouAddANewActionExitCode) {
-  if (kActionCodeUmaReportedMax != 41) {
+  if (kActionCodeUmaReportedMax != 42) {
     LOG(ERROR) << "The following failure is intentional. If you added a new "
                << "ActionExitCode enum value, make sure to add it to the "
                << "PayloadState::UpdateFailed method and then update this test "
diff --git a/postinstall_runner_action.cc b/postinstall_runner_action.cc
index 7ee4143..1baa9ec 100644
--- a/postinstall_runner_action.cc
+++ b/postinstall_runner_action.cc
@@ -16,6 +16,9 @@
 
 namespace {
 const char kPostinstallScript[] = "/postinst";
+const char kPowerwashMarkerFile[] =
+  "/mnt/stateful_partition/factory_install_reset";
+const char kPowerwashCommand[] = "safe fast\n";
 }
 
 void PostinstallRunnerAction::PerformAction() {
@@ -75,10 +78,26 @@
     }
     return;
   }
-  if (HasOutputPipe()) {
-    CHECK(HasInputObject());
-    SetOutputObject(GetInputObject());
+
+  LOG(INFO) << "Postinst command succeeded";
+  CHECK(HasInputObject());
+  const InstallPlan install_plan = GetInputObject();
+
+  if (install_plan.powerwash_required) {
+    if (utils::WriteFile(kPowerwashMarkerFile,
+                         kPowerwashCommand,
+                         strlen(kPowerwashCommand))) {
+      LOG(INFO) << "Configured clobber-state to do powerwash on next reboot";
+    } else {
+      LOG(ERROR) << "Error in configuring clobber-state to do powerwash";
+      completer.set_code(kActionCodePostinstallPowerwashError);
+      return;
+    }
   }
+
+  if (HasOutputPipe())
+    SetOutputObject(install_plan);
+
   completer.set_code(kActionCodeSuccess);
 }
 
diff --git a/real_system_state.h b/real_system_state.h
index 871a942..ecb2fc0 100644
--- a/real_system_state.h
+++ b/real_system_state.h
@@ -58,6 +58,12 @@
     return update_attempter_.get();
   }
 
+  // Returns a pointer to the object that stores the parameters that are
+  // common to all Omaha requests.
+  virtual inline OmahaRequestParams* request_params() {
+    return &request_params_;
+  }
+
   // Initializes this concrete object. Other methods should be invoked only
   // if the object has been initialized successfully.
   bool Initialize(bool enable_gpio);
@@ -92,6 +98,9 @@
 
   // Pointer to the update attempter object.
   scoped_ptr<UpdateAttempter> update_attempter_;
+
+  // Common parameters for all Omaha requests.
+  OmahaRequestParams request_params_;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/system_state.cc b/system_state.cc
index 42737bb..0d553df 100644
--- a/system_state.cc
+++ b/system_state.cc
@@ -13,7 +13,8 @@
 
 RealSystemState::RealSystemState()
     : device_policy_(NULL),
-      connection_manager_(this) {}
+      connection_manager_(this),
+      request_params_(this) {}
 
 bool RealSystemState::Initialize(bool enable_gpio) {
   metrics_lib_.Init();
diff --git a/system_state.h b/system_state.h
index cb33ce0..4b30d1a 100644
--- a/system_state.h
+++ b/system_state.h
@@ -10,6 +10,7 @@
 #include <policy/libpolicy.h>
 
 #include "update_engine/gpio_handler.h"
+#include "update_engine/omaha_request_params.h"
 
 namespace chromeos_update_engine {
 
@@ -59,6 +60,10 @@
 
   // Returns a pointer to the update attempter object.
   virtual UpdateAttempter* update_attempter() = 0;
+
+  // Returns a pointer to the object that stores the parameters that are
+  // common to all Omaha requests.
+  virtual OmahaRequestParams* request_params() = 0;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/update_attempter.cc b/update_attempter.cc
index 7446489..2aee0be 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -136,6 +136,7 @@
       is_test_mode_(false),
       is_test_update_attempted_(false) {
   prefs_ = system_state->prefs();
+  omaha_request_params_ = system_state->request_params();
   if (utils::FileExists(kUpdateCompletedMarker))
     status_ = UPDATE_STATUS_UPDATED_NEED_REBOOT;
 }
@@ -198,16 +199,19 @@
     policy_provider_.reset(new policy::PolicyProvider());
   policy_provider_->Reload();
 
-  // If the release_track is specified by policy, that takes precedence.
-  string release_track;
-
   if (policy_provider_->device_policy_is_loaded()) {
+    LOG(INFO) << "Device policies/settings present";
+
     const policy::DevicePolicy& device_policy =
                                 policy_provider_->GetDevicePolicy();
-    device_policy.GetReleaseChannel(&release_track);
-    device_policy.GetUpdateDisabled(&omaha_request_params_.update_disabled);
-    device_policy.GetTargetVersionPrefix(
-      &omaha_request_params_.target_version_prefix);
+
+    bool update_disabled;
+    device_policy.GetUpdateDisabled(&update_disabled);
+    omaha_request_params_->set_update_disabled(update_disabled);
+
+    string target_version_prefix;
+    device_policy.GetTargetVersionPrefix(&target_version_prefix);
+    omaha_request_params_->set_target_version_prefix(target_version_prefix);
 
     system_state_->set_device_policy(&device_policy);
 
@@ -222,7 +226,7 @@
     LOG(INFO) << "Networks over which updates are allowed per policy : "
               << (allowed_types_str.empty() ? "all" : allowed_types_str);
   } else {
-    LOG(INFO) << "No device policies present.";
+    LOG(INFO) << "No device policies/settings present.";
     system_state_->set_device_policy(NULL);
   }
 
@@ -235,27 +239,51 @@
     LOG(INFO) << "using alternative server address: " << omaha_url_to_use;
   }
 
-  if (!omaha_request_params_.Init(app_version,
-                                  omaha_url_to_use,
-                                  release_track,
-                                  interactive)) {
+  if (!omaha_request_params_->Init(app_version,
+                                   omaha_url_to_use,
+                                   interactive)) {
     LOG(ERROR) << "Unable to initialize Omaha request device params.";
     return false;
   }
 
+  // Set the target channel iff ReleaseChannelDelegated policy is set to
+  // false and a non-empty ReleaseChannel policy is present. If delegated
+  // is true, we'll ignore ReleaseChannel policy value.
+  if (system_state_->device_policy()) {
+    bool delegated = false;
+    system_state_->device_policy()->GetReleaseChannelDelegated(&delegated);
+    if (delegated) {
+      LOG(INFO) << "Channel settings are delegated to user by policy. "
+                   "Ignoring ReleaseChannel policy value";
+    }
+    else {
+      LOG(INFO) << "Channel settings are not delegated to the user by policy";
+      string target_channel;
+      system_state_->device_policy()->GetReleaseChannel(&target_channel);
+      if (target_channel.empty()) {
+        LOG(INFO) << "No ReleaseChannel specified in policy";
+      } else {
+        // Pass in false for powerwash_allowed until we add it to the policy
+        // protobuf.
+        LOG(INFO) << "Setting target channel from ReleaseChannel policy value";
+        omaha_request_params_->SetTargetChannel(target_channel, false);
+      }
+    }
+  }
+
   LOG(INFO) << "update_disabled = "
-            << (omaha_request_params_.update_disabled ? "true" : "false")
+            << utils::ToString(omaha_request_params_->update_disabled())
             << ", target_version_prefix = "
-            << omaha_request_params_.target_version_prefix
+            << omaha_request_params_->target_version_prefix()
             << ", scatter_factor_in_seconds = "
             << utils::FormatSecs(scatter_factor_.InSeconds());
 
   LOG(INFO) << "Wall Clock Based Wait Enabled = "
-            << omaha_request_params_.wall_clock_based_wait_enabled
+            << omaha_request_params_->wall_clock_based_wait_enabled()
             << ", Update Check Count Wait Enabled = "
-            << omaha_request_params_.update_check_count_wait_enabled
+            << omaha_request_params_->update_check_count_wait_enabled()
             << ", Waiting Period = " << utils::FormatSecs(
-               omaha_request_params_.waiting_period.InSeconds());
+               omaha_request_params_->waiting_period().InSeconds());
 
   obeying_proxies_ = true;
   if (obey_proxies || proxy_manual_checks_ == 0) {
@@ -308,11 +336,11 @@
     // Now check if we need to update the waiting period. The two cases
     // in which we'd need to update the waiting period are:
     // 1. First time in process or a scheduled check after a user-initiated one.
-    //    (omaha_request_params_.waiting_period will be zero in this case).
+    //    (omaha_request_params_->waiting_period will be zero in this case).
     // 2. Admin has changed the scattering policy value.
     //    (new scattering value will be different from old one in this case).
     int64 wait_period_in_secs = 0;
-    if (omaha_request_params_.waiting_period.InSeconds() == 0) {
+    if (omaha_request_params_->waiting_period().InSeconds() == 0) {
       // First case. Check if we have a suitable value to set for
       // the waiting period.
       if (prefs_->GetInt64(kPrefsWallClockWaitPeriod, &wait_period_in_secs) &&
@@ -324,10 +352,11 @@
         // So, in this case, we should reuse the persisted value instead of
         // generating a new random value to improve the chances of a good
         // distribution for scattering.
-        omaha_request_params_.waiting_period =
-          TimeDelta::FromSeconds(wait_period_in_secs);
+        omaha_request_params_->set_waiting_period(
+          TimeDelta::FromSeconds(wait_period_in_secs));
         LOG(INFO) << "Using persisted wall-clock waiting period: " <<
-            utils::FormatSecs(omaha_request_params_.waiting_period.InSeconds());
+            utils::FormatSecs(
+                omaha_request_params_->waiting_period().InSeconds());
       }
       else {
         // This means there's no persisted value for the waiting period
@@ -349,30 +378,32 @@
       // Neither the first time scattering is enabled nor the scattering value
       // changed. Nothing to do.
       LOG(INFO) << "Keeping current wall-clock waiting period: " <<
-          utils::FormatSecs(omaha_request_params_.waiting_period.InSeconds());
+          utils::FormatSecs(
+              omaha_request_params_->waiting_period().InSeconds());
     }
 
-    // The invariant at this point is that omaha_request_params_.waiting_period
+    // The invariant at this point is that omaha_request_params_->waiting_period
     // is non-zero no matter which path we took above.
-    LOG_IF(ERROR, omaha_request_params_.waiting_period.InSeconds() == 0)
+    LOG_IF(ERROR, omaha_request_params_->waiting_period().InSeconds() == 0)
         << "Waiting Period should NOT be zero at this point!!!";
 
     // Since scattering is enabled, wall clock based wait will always be
     // enabled.
-    omaha_request_params_.wall_clock_based_wait_enabled = true;
+    omaha_request_params_->set_wall_clock_based_wait_enabled(true);
 
     // If we don't have any issues in accessing the file system to update
     // the update check count value, we'll turn that on as well.
     bool decrement_succeeded = DecrementUpdateCheckCount();
-    omaha_request_params_.update_check_count_wait_enabled = decrement_succeeded;
+    omaha_request_params_->set_update_check_count_wait_enabled(
+      decrement_succeeded);
   } else {
     // This means the scattering feature is turned off or disabled for
     // this particular update check. Make sure to disable
     // all the knobs and artifacts so that we don't invoke any scattering
     // related code.
-    omaha_request_params_.wall_clock_based_wait_enabled = false;
-    omaha_request_params_.update_check_count_wait_enabled = false;
-    omaha_request_params_.waiting_period = TimeDelta::FromSeconds(0);
+    omaha_request_params_->set_wall_clock_based_wait_enabled(false);
+    omaha_request_params_->set_update_check_count_wait_enabled(false);
+    omaha_request_params_->set_waiting_period(TimeDelta::FromSeconds(0));
     prefs_->Delete(kPrefsWallClockWaitPeriod);
     prefs_->Delete(kPrefsUpdateCheckCount);
     // Don't delete the UpdateFirstSeenAt file as we don't want manual checks
@@ -383,18 +414,18 @@
 }
 
 void UpdateAttempter::GenerateNewWaitingPeriod() {
-  omaha_request_params_.waiting_period = TimeDelta::FromSeconds(
-      base::RandInt(1, scatter_factor_.InSeconds()));
+  omaha_request_params_->set_waiting_period(TimeDelta::FromSeconds(
+      base::RandInt(1, scatter_factor_.InSeconds())));
 
   LOG(INFO) << "Generated new wall-clock waiting period: " << utils::FormatSecs(
-                omaha_request_params_.waiting_period.InSeconds());
+                omaha_request_params_->waiting_period().InSeconds());
 
   // Do a best-effort to persist this in all cases. Even if the persistence
   // fails, we'll still be able to scatter based on our in-memory value.
   // The persistence only helps in ensuring a good overall distribution
   // across multiple devices if they tend to reboot too often.
   prefs_->SetInt64(kPrefsWallClockWaitPeriod,
-                   omaha_request_params_.waiting_period.InSeconds());
+                   omaha_request_params_->waiting_period().InSeconds());
 }
 
 void UpdateAttempter::BuildUpdateActions(bool interactive) {
@@ -410,7 +441,6 @@
   update_check_fetcher->set_check_certificate(CertificateChecker::kUpdate);
   shared_ptr<OmahaRequestAction> update_check_action(
       new OmahaRequestAction(system_state_,
-                             &omaha_request_params_,
                              NULL,
                              update_check_fetcher,  // passes ownership
                              false));
@@ -422,7 +452,6 @@
       new FilesystemCopierAction(true, false));
   shared_ptr<OmahaRequestAction> download_started_action(
       new OmahaRequestAction(system_state_,
-                             &omaha_request_params_,
                              new OmahaEvent(
                                  OmahaEvent::kTypeUpdateDownloadStarted),
                              new LibcurlHttpFetcher(GetProxyResolver(),
@@ -439,7 +468,6 @@
                              download_fetcher)));  // passes ownership
   shared_ptr<OmahaRequestAction> download_finished_action(
       new OmahaRequestAction(system_state_,
-                             &omaha_request_params_,
                              new OmahaEvent(
                                  OmahaEvent::kTypeUpdateDownloadFinished),
                              new LibcurlHttpFetcher(GetProxyResolver(),
@@ -454,7 +482,6 @@
       new PostinstallRunnerAction);
   shared_ptr<OmahaRequestAction> update_complete_action(
       new OmahaRequestAction(system_state_,
-                             &omaha_request_params_,
                              new OmahaEvent(OmahaEvent::kTypeUpdateComplete),
                              new LibcurlHttpFetcher(GetProxyResolver(),
                                                     system_state_,
@@ -568,7 +595,8 @@
   if (code == kActionCodeSuccess) {
     utils::WriteFile(kUpdateCompletedMarker, "", 0);
     prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0);
-    prefs_->SetString(kPrefsPreviousVersion, omaha_request_params_.app_version);
+    prefs_->SetString(kPrefsPreviousVersion,
+                      omaha_request_params_->app_version());
     DeltaPerformer::ResetUpdateProgress(prefs_, false);
 
     // Since we're done with scattering fully at this point, this is the
@@ -822,7 +850,7 @@
   if (!utils::IsOfficialBuild())
     flags |= kActionCodeTestImageFlag;
 
-  if (omaha_request_params_.update_url != kProductionOmahaUrl)
+  if (omaha_request_params_->update_url() != kProductionOmahaUrl)
     flags |= kActionCodeTestOmahaUrlFlag;
 
   return flags;
@@ -896,7 +924,6 @@
   // Send it to Omaha.
   shared_ptr<OmahaRequestAction> error_event_action(
       new OmahaRequestAction(system_state_,
-                             &omaha_request_params_,
                              error_event_.release(),  // Pass ownership.
                              new LibcurlHttpFetcher(GetProxyResolver(),
                                                     system_state_,
@@ -966,11 +993,11 @@
 
 void UpdateAttempter::DisableDeltaUpdateIfNeeded() {
   int64_t delta_failures;
-  if (omaha_request_params_.delta_okay &&
+  if (omaha_request_params_->delta_okay() &&
       prefs_->GetInt64(kPrefsDeltaUpdateFailures, &delta_failures) &&
       delta_failures >= kMaxDeltaUpdateFailures) {
     LOG(WARNING) << "Too many delta update failures, forcing full update.";
-    omaha_request_params_.delta_okay = false;
+    omaha_request_params_->set_delta_okay(false);
   }
 }
 
@@ -1012,7 +1039,6 @@
   if (!processor_->IsRunning()) {
     shared_ptr<OmahaRequestAction> ping_action(
         new OmahaRequestAction(system_state_,
-                               &omaha_request_params_,
                                NULL,
                                new LibcurlHttpFetcher(GetProxyResolver(),
                                                       system_state_,
diff --git a/update_attempter.h b/update_attempter.h
index 79c4c65..23b45c0 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -253,8 +253,9 @@
   // only after the device policy has been loaded and set in the system_state_.
   void CalculateScatteringParams(bool is_interactive);
 
-  // Sets a random value for the omaha_request_params_.waiting_period
-  // based on the current scatter_factor_ value.
+  // Sets a random value for the waiting period to wait for before downloading
+  // an update, if one available. This value will be upperbounded by the
+  // scatter factor value specified from policy.
   void GenerateNewWaitingPeriod();
 
   // Helper method of Update() to construct the sequence of actions to
@@ -322,8 +323,8 @@
   std::string new_version_;
   int64_t new_payload_size_;
 
-  // Device paramaters common to all Omaha requests.
-  OmahaRequestDeviceParams omaha_request_params_;
+  // Common parameters for all Omaha requests.
+  OmahaRequestParams* omaha_request_params_;
 
   // Number of consecutive manual update checks we've had where we obeyed
   // Chrome's proxy settings.
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 39ad109..042690f 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -80,8 +80,8 @@
   void PingOmahaTestStart();
   static gboolean StaticPingOmahaTestStart(gpointer data);
 
-  void ReadTrackFromPolicyTestStart();
-  static gboolean StaticReadTrackFromPolicyTestStart(gpointer data);
+  void ReadChannelFromPolicyTestStart();
+  static gboolean StaticReadChannelFromPolicyTestStart(gpointer data);
 
   void ReadUpdateDisabledFromPolicyTestStart();
   static gboolean StaticReadUpdateDisabledFromPolicyTestStart(gpointer data);
@@ -135,8 +135,7 @@
 TEST_F(UpdateAttempterTest, ActionCompletedOmahaRequestTest) {
   scoped_ptr<MockHttpFetcher> fetcher(new MockHttpFetcher("", 0, NULL));
   fetcher->FailTransfer(500);  // Sets the HTTP response code.
-  OmahaRequestParams params;
-  OmahaRequestAction action(&mock_system_state_, &params, NULL,
+  OmahaRequestAction action(&mock_system_state_, NULL,
                             fetcher.release(), false);
   ObjectCollectorAction<OmahaResponse> collector_action;
   BondActions(&action, &collector_action);
@@ -168,9 +167,8 @@
   EXPECT_EQ(kActionCodeSuccess,
             GetErrorCodeForAction(NULL, kActionCodeSuccess));
 
-  OmahaRequestParams params;
   MockSystemState mock_system_state;
-  OmahaRequestAction omaha_request_action(&mock_system_state, &params, NULL,
+  OmahaRequestAction omaha_request_action(&mock_system_state, NULL,
                                           NULL, false);
   EXPECT_EQ(kActionCodeOmahaRequestError,
             GetErrorCodeForAction(&omaha_request_action, kActionCodeError));
@@ -192,26 +190,26 @@
 }
 
 TEST_F(UpdateAttempterTest, DisableDeltaUpdateIfNeededTest) {
-  attempter_.omaha_request_params_.delta_okay = true;
+  attempter_.omaha_request_params_->set_delta_okay(true);
   EXPECT_CALL(*prefs_, GetInt64(kPrefsDeltaUpdateFailures, _))
       .WillOnce(Return(false));
   attempter_.DisableDeltaUpdateIfNeeded();
-  EXPECT_TRUE(attempter_.omaha_request_params_.delta_okay);
+  EXPECT_TRUE(attempter_.omaha_request_params_->delta_okay());
   EXPECT_CALL(*prefs_, GetInt64(kPrefsDeltaUpdateFailures, _))
       .WillOnce(DoAll(
           SetArgumentPointee<1>(UpdateAttempter::kMaxDeltaUpdateFailures - 1),
           Return(true)));
   attempter_.DisableDeltaUpdateIfNeeded();
-  EXPECT_TRUE(attempter_.omaha_request_params_.delta_okay);
+  EXPECT_TRUE(attempter_.omaha_request_params_->delta_okay());
   EXPECT_CALL(*prefs_, GetInt64(kPrefsDeltaUpdateFailures, _))
       .WillOnce(DoAll(
           SetArgumentPointee<1>(UpdateAttempter::kMaxDeltaUpdateFailures),
           Return(true)));
   attempter_.DisableDeltaUpdateIfNeeded();
-  EXPECT_FALSE(attempter_.omaha_request_params_.delta_okay);
+  EXPECT_FALSE(attempter_.omaha_request_params_->delta_okay());
   EXPECT_CALL(*prefs_, GetInt64(_, _)).Times(0);
   attempter_.DisableDeltaUpdateIfNeeded();
-  EXPECT_FALSE(attempter_.omaha_request_params_.delta_okay);
+  EXPECT_FALSE(attempter_.omaha_request_params_->delta_okay());
 }
 
 TEST_F(UpdateAttempterTest, MarkDeltaUpdateFailureTest) {
@@ -306,9 +304,10 @@
   return FALSE;
 }
 
-gboolean UpdateAttempterTest::StaticReadTrackFromPolicyTestStart(
+gboolean UpdateAttempterTest::StaticReadChannelFromPolicyTestStart(
     gpointer data) {
-  reinterpret_cast<UpdateAttempterTest*>(data)->ReadTrackFromPolicyTestStart();
+  UpdateAttempterTest* ua_test = reinterpret_cast<UpdateAttempterTest*>(data);
+  ua_test->ReadChannelFromPolicyTestStart();
   return FALSE;
 }
 
@@ -452,30 +451,37 @@
             attempter_.error_event_->error_code);
 }
 
-TEST_F(UpdateAttempterTest, ReadTrackFromPolicy) {
+TEST_F(UpdateAttempterTest, ReadChannelFromPolicy) {
   loop_ = g_main_loop_new(g_main_context_default(), FALSE);
-  g_idle_add(&StaticReadTrackFromPolicyTestStart, this);
+  g_idle_add(&StaticReadChannelFromPolicyTestStart, 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
+void UpdateAttempterTest::ReadChannelFromPolicyTestStart() {
+  // Tests that the update channel (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(mock_system_state_, device_policy()).WillRepeatedly(
+      Return(device_policy));
 
-  EXPECT_CALL(*device_policy, GetReleaseChannel(_))
-      .WillRepeatedly(DoAll(
-          SetArgumentPointee<0>(std::string("canary-channel")),
-          Return(true)));
+  EXPECT_CALL(*device_policy, GetReleaseChannelDelegated(_)).WillRepeatedly(
+      DoAll(SetArgumentPointee<0>(bool(false)),
+      Return(true)));
 
+  EXPECT_CALL(*device_policy, GetReleaseChannel(_)).WillRepeatedly(
+      DoAll(SetArgumentPointee<0>(std::string("beta-channel")),
+      Return(true)));
+
+  attempter_.omaha_request_params_->set_root("./UpdateAttempterTest");
   attempter_.Update("", "", false, false, false);
-  EXPECT_EQ("canary-channel", attempter_.omaha_request_params_.app_track);
+  EXPECT_EQ("beta-channel",
+            attempter_.omaha_request_params_->target_channel());
 
   g_idle_add(&StaticQuitMainLoop, this);
 }
@@ -496,6 +502,8 @@
   attempter_.policy_provider_.reset(new policy::PolicyProvider(device_policy));
 
   EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
+  EXPECT_CALL(mock_system_state_, device_policy()).WillRepeatedly(
+      Return(device_policy));
 
   EXPECT_CALL(*device_policy, GetUpdateDisabled(_))
       .WillRepeatedly(DoAll(
@@ -503,7 +511,7 @@
           Return(true)));
 
   attempter_.Update("", "", false, false, false);
-  EXPECT_TRUE(attempter_.omaha_request_params_.update_disabled);
+  EXPECT_TRUE(attempter_.omaha_request_params_->update_disabled());
 
   g_idle_add(&StaticQuitMainLoop, this);
 }
@@ -526,6 +534,8 @@
   attempter_.policy_provider_.reset(new policy::PolicyProvider(device_policy));
 
   EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
+  EXPECT_CALL(mock_system_state_, device_policy()).WillRepeatedly(
+      Return(device_policy));
 
   EXPECT_CALL(*device_policy, GetTargetVersionPrefix(_))
       .WillRepeatedly(DoAll(
@@ -534,7 +544,7 @@
 
   attempter_.Update("", "", false, false, false);
   EXPECT_EQ(target_version_prefix.c_str(),
-            attempter_.omaha_request_params_.target_version_prefix);
+            attempter_.omaha_request_params_->target_version_prefix());
 
   g_idle_add(&StaticQuitMainLoop, this);
 }
@@ -622,7 +632,8 @@
   EXPECT_TRUE(prefs.GetInt64(kPrefsUpdateCheckCount, &new_value));
   EXPECT_EQ(initial_value - 1, new_value);
 
-  EXPECT_TRUE(attempter_.omaha_request_params_.update_check_count_wait_enabled);
+  EXPECT_TRUE(
+      attempter_.omaha_request_params_->update_check_count_wait_enabled());
 
   // However, if the count is already 0, it's not decremented. Test that.
   initial_value = 0;
@@ -685,11 +696,12 @@
 
   // Make sure scattering is disabled for manual (i.e. user initiated) update
   // checks and all artifacts are removed.
-  EXPECT_FALSE(attempter_.omaha_request_params_.wall_clock_based_wait_enabled);
+  EXPECT_FALSE(
+      attempter_.omaha_request_params_->wall_clock_based_wait_enabled());
   EXPECT_FALSE(prefs.Exists(kPrefsWallClockWaitPeriod));
-  EXPECT_TRUE(attempter_.omaha_request_params_.waiting_period.InSeconds() == 0);
-  EXPECT_FALSE(attempter_.omaha_request_params_.
-                 update_check_count_wait_enabled);
+  EXPECT_EQ(0, attempter_.omaha_request_params_->waiting_period().InSeconds());
+  EXPECT_FALSE(
+      attempter_.omaha_request_params_->update_check_count_wait_enabled());
   EXPECT_FALSE(prefs.Exists(kPrefsUpdateCheckCount));
 
   g_idle_add(&StaticQuitMainLoop, this);
diff --git a/update_engine.xml b/update_engine.xml
index f226fa4..0ce0962 100644
--- a/update_engine.xml
+++ b/update_engine.xml
@@ -20,14 +20,23 @@
       <arg type="s" name="new_version" direction="out" />
       <arg type="x" name="new_size" direction="out" />
     </method>
+    <method name="RebootIfNeeded">
+    </method>
+    <!-- Deprecate GetTrack and SetTrack after updating the clients -->
     <method name="GetTrack">
       <arg type="s" name="track" direction="out" />
     </method>
-    <method name="RebootIfNeeded">
-    </method>
     <method name="SetTrack">
       <arg type="s" name="track" />
     </method>
+    <method name="SetChannel">
+      <arg type="s" name="target_channel" />
+      <arg type="b" name="is_powerwash_allowed" />
+    </method>
+    <method name="GetChannel">
+      <arg type="b" name="get_current_channel" />
+      <arg type="s" name="channel" direction="out" />
+    </method>
     <signal name="StatusUpdate">
       <arg type="x" name="last_checked_time" />
       <arg type="d" name="progress" />
diff --git a/update_engine_client.cc b/update_engine_client.cc
index f3b5eea..e28801c 100644
--- a/update_engine_client.cc
+++ b/update_engine_client.cc
@@ -26,10 +26,12 @@
 DEFINE_bool(check_for_update, false, "Initiate check for updates.");
 DEFINE_string(omaha_url, "", "The URL of the Omaha update server.");
 DEFINE_bool(reboot, false, "Initiate a reboot if needed.");
-DEFINE_bool(show_track, false, "Show the update track.");
+DEFINE_bool(show_channel, false, "Show the current and target channels.");
 DEFINE_bool(status, false, "Print the status to stdout.");
 DEFINE_bool(reset_status, false, "Sets the status in update_engine to idle.");
-DEFINE_string(track, "", "Permanently change the update track.");
+DEFINE_string(channel, "",
+    "Set the target channel. The device will be powerwashed if the target "
+    "channel is more stable than the current channel.");
 DEFINE_bool(update, false, "Forces an update and waits for its completion. "
             "Exit status is 0 if the update succeeded, and 1 otherwise.");
 DEFINE_bool(watch_for_updates, false,
@@ -205,35 +207,38 @@
   return true;
 }
 
-void SetTrack(const string& track) {
+void SetTargetChannel(const string& target_channel) {
   DBusGProxy* proxy;
   GError* error = NULL;
 
   CHECK(GetProxy(&proxy));
 
   gboolean rc =
-      org_chromium_UpdateEngineInterface_set_track(proxy,
-                                                   track.c_str(),
-                                                   &error);
-  CHECK_EQ(rc, true) << "Error setting the track: "
+      org_chromium_UpdateEngineInterface_set_channel(proxy,
+                                                     target_channel.c_str(),
+                                                     true, // OK to Powerwash
+                                                     &error);
+  CHECK_EQ(rc, true) << "Error setting the channel: "
                      << GetAndFreeGError(&error);
-  LOG(INFO) << "Track permanently set to: " << track;
+  LOG(INFO) << "Channel permanently set to: " << target_channel;
 }
 
-string GetTrack() {
+string GetChannel(bool get_current_channel) {
   DBusGProxy* proxy;
   GError* error = NULL;
 
   CHECK(GetProxy(&proxy));
 
-  char* track = NULL;
+  char* channel = NULL;
   gboolean rc =
-      org_chromium_UpdateEngineInterface_get_track(proxy,
-                                                   &track,
-                                                   &error);
-  CHECK_EQ(rc, true) << "Error getting the track: " << GetAndFreeGError(&error);
-  string output = track;
-  g_free(track);
+      org_chromium_UpdateEngineInterface_get_channel(proxy,
+                                                     get_current_channel,
+                                                     &channel,
+                                                     &error);
+  CHECK_EQ(rc, true) << "Error getting the channel: "
+                     << GetAndFreeGError(&error);
+  string output = channel;
+  g_free(channel);
   return output;
 }
 
@@ -294,14 +299,18 @@
     return 0;
   }
 
-  // First, update the track if requested.
-  if (!FLAGS_track.empty()) {
-    SetTrack(FLAGS_track);
-  }
+  // First, update the target channel if requested.
+  if (!FLAGS_channel.empty())
+    SetTargetChannel(FLAGS_channel);
 
-  // Show the track if requested.
-  if (FLAGS_show_track) {
-    LOG(INFO) << "Track: " << GetTrack();
+  // Show the current and target channels if requested.
+  if (FLAGS_show_channel) {
+    string current_channel = GetChannel(true);
+    LOG(INFO) << "Current Channel: " << current_channel;
+
+    string target_channel = GetChannel(false);
+    if (!target_channel.empty())
+      LOG(INFO) << "Target Channel (pending update): " << target_channel;
   }
 
   // Initiate an update check, if necessary.
diff --git a/utils.cc b/utils.cc
index 2276ac4..9337cc7 100644
--- a/utils.cc
+++ b/utils.cc
@@ -713,6 +713,10 @@
                       exp_time.second);
 }
 
+string ToString(bool b) {
+  return (b ? "true" : "false");
+}
+
 ActionExitCode GetBaseErrorCode(ActionExitCode code) {
   // Ignore the higher order bits in the code by applying the mask as
   // we want the enumerations to be in the small contiguous range
@@ -867,6 +871,8 @@
       return "kActionCodeDownloadMetadataSignatureMissingError";
     case kActionCodeOmahaUpdateDeferredForBackoff:
       return "kActionCodeOmahaUpdateDeferredForBackoff";
+    case kActionCodePostinstallPowerwashError:
+      return "kActionCodePostinstallPowerwashError";
     case kActionCodeUmaReportedMax:
       return "kActionCodeUmaReportedMax";
     case kActionCodeOmahaRequestHTTPResponseBase:
diff --git a/utils.h b/utils.h
index 569556c..badaa5f 100644
--- a/utils.h
+++ b/utils.h
@@ -148,6 +148,9 @@
 // such as "11/14/2011 14:05:30 GMT".
 std::string ToString(const base::Time utc_time);
 
+// Returns true or false depending on the value of b.
+std::string ToString(bool b);
+
 enum BootLoader {
   BootLoader_SYSLINUX = 0,
   BootLoader_CHROME_FIRMWARE = 1