Merge master into aosp-master

3e86099 Replace ' in json constants with "
8b00597 Implement privet properties for changing cloud print endpoints
1e019fa Replace URL with Url in identifiers
03ee8ac Enable support of endpoints override.

Change-Id: I7732dce63d7c349aa71093dff2cc3db2cb94569a
diff --git a/examples/daemon/common/daemon.h b/examples/daemon/common/daemon.h
index 985c5e5..0b3c25a 100644
--- a/examples/daemon/common/daemon.h
+++ b/examples/daemon/common/daemon.h
@@ -19,10 +19,11 @@
 class Daemon {
  public:
   struct Options {
-    bool force_bootstrapping_{false};
-    bool disable_privet_{false};
-    std::string registration_ticket_;
-    std::string model_id_{"AAAAA"};
+    bool force_bootstrapping{false};
+    bool disable_privet{false};
+    std::string registration_ticket;
+    std::string model_id{"AAAAA"};
+    std::string service_url;
 
     static void ShowUsage(const std::string& name) {
       LOG(ERROR) << "\nUsage: " << name << " <option(s)>"
@@ -30,8 +31,10 @@
                  << "\t-h,--help                    Show this help message\n"
                  << "\t--v=LEVEL                    Logging level\n"
                  << "\t-b,--bootstrapping           Force WiFi bootstrapping\n"
-                 << "\t--registration_ticket=TICKET Register device with the "
-                    "given ticket\n"
+                 << "\t-r,--registration_ticket=TICKET Register device with "
+                    "the given ticket\n"
+                 << "\t-s,--staging                 Use staging server. Use "
+                    "only with -r.\n"
                  << "\t--disable_privet             Disable local privet\n";
     }
 
@@ -41,15 +44,19 @@
         if (arg == "-h" || arg == "--help") {
           return false;
         } else if (arg == "-b" || arg == "--bootstrapping") {
-          force_bootstrapping_ = true;
+          force_bootstrapping = true;
+        } else if (arg == "-s" || arg == "--staging") {
+          service_url =
+              "https://www-googleapis-staging.sandbox.google.com/weave/v1/";
         } else if (arg == "--disable_privet") {
-          disable_privet_ = true;
-        } else if (arg.find("--registration_ticket") != std::string::npos) {
+          disable_privet = true;
+        } else if (arg.find("--registration_ticket=") != std::string::npos ||
+                   arg.find("-r=") != std::string::npos) {
           auto pos = arg.find("=");
           if (pos == std::string::npos) {
             return false;
           }
-          registration_ticket_ = arg.substr(pos + 1);
+          registration_ticket = arg.substr(pos + 1);
         } else if (arg.find("--v") != std::string::npos) {
           auto pos = arg.find("=");
           if (pos == std::string::npos) {
@@ -66,14 +73,13 @@
 
   Daemon(const Options& opts)
       : task_runner_{new weave::examples::EventTaskRunner},
-        config_store_{
-            new weave::examples::FileConfigStore(opts.model_id_,
-                                                 task_runner_.get())},
+        config_store_{new weave::examples::FileConfigStore(opts.model_id,
+                                                           task_runner_.get())},
         http_client_{new weave::examples::CurlHttpClient(task_runner_.get())},
         network_{new weave::examples::EventNetworkImpl(task_runner_.get())},
         bluetooth_{new weave::examples::BluetoothImpl} {
-    if (!opts.disable_privet_) {
-      network_->SetSimulateOffline(opts.force_bootstrapping_);
+    if (!opts.disable_privet) {
+      network_->SetSimulateOffline(opts.force_bootstrapping);
 
       dns_sd_.reset(new weave::examples::AvahiClient);
       http_server_.reset(
@@ -87,9 +93,11 @@
                                     dns_sd_.get(), http_server_.get(),
                                     wifi_.get(), bluetooth_.get());
 
-    if (!opts.registration_ticket_.empty()) {
-      device_->Register(opts.registration_ticket_,
-                        base::Bind(&OnRegisterDeviceDone, device_.get()));
+    if (!opts.registration_ticket.empty()) {
+      weave::RegistrationData data;
+      data.ticket_id = opts.registration_ticket;
+      data.service_url = opts.service_url;
+      device_->Register(data, base::Bind(&OnRegisterDeviceDone, device_.get()));
     }
   }
 
diff --git a/examples/daemon/light/light.cc b/examples/daemon/light/light.cc
index a78231f..4cbf9b4 100644
--- a/examples/daemon/light/light.cc
+++ b/examples/daemon/light/light.cc
@@ -304,7 +304,7 @@
 
 int main(int argc, char** argv) {
   Daemon::Options opts;
-  opts.model_id_ = "AIAAA";
+  opts.model_id = "AIAAA";
   if (!opts.Parse(argc, argv)) {
     Daemon::Options::ShowUsage(argv[0]);
     return 1;
diff --git a/examples/daemon/lock/lock.cc b/examples/daemon/lock/lock.cc
index fcc6ad7..a2da9cf 100644
--- a/examples/daemon/lock/lock.cc
+++ b/examples/daemon/lock/lock.cc
@@ -134,7 +134,7 @@
 
 int main(int argc, char** argv) {
   Daemon::Options opts;
-  opts.model_id_ = "AOAAA";
+  opts.model_id = "AOAAA";
   if (!opts.Parse(argc, argv)) {
     Daemon::Options::ShowUsage(argv[0]);
     return 1;
diff --git a/examples/provider/file_config_store.cc b/examples/provider/file_config_store.cc
index b215023..af3f047 100644
--- a/examples/provider/file_config_store.cc
+++ b/examples/provider/file_config_store.cc
@@ -48,6 +48,7 @@
   settings->model_id = model_id_;
   settings->pairing_modes = {PairingType::kEmbeddedCode};
   settings->embedded_code = "0000";
+  settings->allow_endpoints_override = true;
 
   // Keys owners:
   //   avakulenko@google.com
diff --git a/include/weave/device.h b/include/weave/device.h
index 05e3b78..9a29574 100644
--- a/include/weave/device.h
+++ b/include/weave/device.h
@@ -31,6 +31,31 @@
   kInvalidCredentials,  // Our registration has been revoked.
 };
 
+struct RegistrationData {
+  explicit RegistrationData(const std::string& ticket = {})
+      : ticket_id{ticket} {}
+
+  std::string ticket_id;
+
+  std::string oauth_url;
+  std::string client_id;
+  std::string client_secret;
+  std::string api_key;
+  std::string service_url;
+  std::string xmpp_endpoint;
+};
+
+inline bool operator==(const RegistrationData& l, const RegistrationData& r) {
+  return l.ticket_id == r.ticket_id && l.oauth_url == r.oauth_url &&
+         l.client_id == r.client_id && l.client_secret == r.client_secret &&
+         l.api_key == r.api_key && l.service_url == r.service_url &&
+         l.xmpp_endpoint == r.xmpp_endpoint;
+}
+
+inline bool operator!=(const RegistrationData& l, const RegistrationData& r) {
+  return !(l == r);
+}
+
 class Device {
  public:
   virtual ~Device() {}
@@ -141,7 +166,7 @@
 
   // Registers the device.
   // This is testing method and should not be used by applications.
-  virtual void Register(const std::string& ticket_id,
+  virtual void Register(const RegistrationData& registration_data,
                         const DoneCallback& callback) = 0;
 
   // Handler should display pin code to the user.
diff --git a/include/weave/settings.h b/include/weave/settings.h
index 7cb798d..2ba619c 100644
--- a/include/weave/settings.h
+++ b/include/weave/settings.h
@@ -71,6 +71,7 @@
 
   // Internal options to tweak some library functionality. External code should
   // avoid using them.
+  bool allow_endpoints_override{false};
   bool wifi_auto_setup_enabled{true};
   std::string test_privet_ssid;
 };
diff --git a/include/weave/test/mock_device.h b/include/weave/test/mock_device.h
index 2f380e0..8e75d44 100644
--- a/include/weave/test/mock_device.h
+++ b/include/weave/test/mock_device.h
@@ -63,7 +63,7 @@
   MOCK_METHOD1(AddGcdStateChangedCallback,
                void(const GcdStateChangedCallback& callback));
   MOCK_METHOD2(Register,
-               void(const std::string& ticket_id,
+               void(const RegistrationData& registration_data,
                     const DoneCallback& callback));
   MOCK_METHOD2(AddPairingChangedCallbacks,
                void(const PairingBeginCallback& begin_callback,
diff --git a/src/config.cc b/src/config.cc
index 21a1c1f..cd28de9 100644
--- a/src/config.cc
+++ b/src/config.cc
@@ -31,8 +31,8 @@
 const char kClientId[] = "client_id";
 const char kClientSecret[] = "client_secret";
 const char kApiKey[] = "api_key";
-const char kOAuthURL[] = "oauth_url";
-const char kServiceURL[] = "service_url";
+const char kOAuthUrl[] = "oauth_url";
+const char kServiceUrl[] = "service_url";
 const char kXmppEndpoint[] = "xmpp_endpoint";
 const char kName[] = "name";
 const char kDescription[] = "description";
@@ -67,7 +67,7 @@
     dict->Set(config_keys::kCloudId, std::move(tmp));
 }
 
-Config::Settings CreateDefaultSettings() {
+Config::Settings CreateDefaultSettings(provider::ConfigStore* config_store) {
   Config::Settings result;
   result.oauth_url = "https://accounts.google.com/o/oauth2/";
   result.service_url = kWeaveUrl;
@@ -75,6 +75,36 @@
   result.local_anonymous_access_role = AuthScope::kViewer;
   result.pairing_modes.insert(PairingType::kPinCode);
   result.device_id = base::GenerateGUID();
+
+  if (!config_store)
+    return result;
+
+  // Crash on any mistakes in defaults.
+  CHECK(config_store->LoadDefaults(&result));
+
+  CHECK(!result.client_id.empty());
+  CHECK(!result.client_secret.empty());
+  CHECK(!result.api_key.empty());
+  CHECK(!result.oauth_url.empty());
+  CHECK(!result.service_url.empty());
+  CHECK(!result.xmpp_endpoint.empty());
+  CHECK(!result.oem_name.empty());
+  CHECK(!result.model_name.empty());
+  CHECK(!result.model_id.empty());
+  CHECK(!result.name.empty());
+  CHECK(!result.device_id.empty());
+  CHECK_EQ(result.embedded_code.empty(),
+           (result.pairing_modes.find(PairingType::kEmbeddedCode) ==
+            result.pairing_modes.end()));
+
+  // Values below will be generated at runtime.
+  CHECK(result.cloud_id.empty());
+  CHECK(result.refresh_token.empty());
+  CHECK(result.robot_account.empty());
+  CHECK(result.last_configured_ssid.empty());
+  CHECK(result.secret.empty());
+  CHECK(result.root_client_token_owner == RootClientTokenOwner::kNone);
+
   return result;
 }
 
@@ -91,8 +121,12 @@
     : EnumToStringMap(kRootClientTokenOwnerMap) {}
 
 Config::Config(provider::ConfigStore* config_store)
-    : settings_{CreateDefaultSettings()}, config_store_{config_store} {
-  Load();
+    : defaults_{CreateDefaultSettings(config_store)},
+      settings_{defaults_},
+      config_store_{config_store} {
+  Transaction change{this};
+  change.save_ = false;
+  change.LoadState();
 }
 
 void Config::AddOnChangedCallback(const OnChangedCallback& callback) {
@@ -105,42 +139,8 @@
   return settings_;
 }
 
-void Config::Load() {
-  Transaction change{this};
-  change.save_ = false;
-
-  settings_ = CreateDefaultSettings();
-
-  if (!config_store_)
-    return;
-
-  // Crash on any mistakes in defaults.
-  CHECK(config_store_->LoadDefaults(&settings_));
-
-  CHECK(!settings_.client_id.empty());
-  CHECK(!settings_.client_secret.empty());
-  CHECK(!settings_.api_key.empty());
-  CHECK(!settings_.oauth_url.empty());
-  CHECK(!settings_.service_url.empty());
-  CHECK(!settings_.xmpp_endpoint.empty());
-  CHECK(!settings_.oem_name.empty());
-  CHECK(!settings_.model_name.empty());
-  CHECK(!settings_.model_id.empty());
-  CHECK(!settings_.name.empty());
-  CHECK(!settings_.device_id.empty());
-  CHECK_EQ(settings_.embedded_code.empty(),
-           (settings_.pairing_modes.find(PairingType::kEmbeddedCode) ==
-              settings_.pairing_modes.end()));
-
-  // Values below will be generated at runtime.
-  CHECK(settings_.cloud_id.empty());
-  CHECK(settings_.refresh_token.empty());
-  CHECK(settings_.robot_account.empty());
-  CHECK(settings_.last_configured_ssid.empty());
-  CHECK(settings_.secret.empty());
-  CHECK(settings_.root_client_token_owner == RootClientTokenOwner::kNone);
-
-  change.LoadState();
+const Config::Settings& Config::GetDefaults() const {
+  return defaults_;
 }
 
 void Config::Transaction::LoadState() {
@@ -185,10 +185,10 @@
   if (dict->GetString(config_keys::kApiKey, &tmp))
     set_api_key(tmp);
 
-  if (dict->GetString(config_keys::kOAuthURL, &tmp))
+  if (dict->GetString(config_keys::kOAuthUrl, &tmp))
     set_oauth_url(tmp);
 
-  if (dict->GetString(config_keys::kServiceURL, &tmp)) {
+  if (dict->GetString(config_keys::kServiceUrl, &tmp)) {
     if (tmp == kDeprecatedUrl)
       tmp = kWeaveUrl;
     set_service_url(tmp);
@@ -255,8 +255,8 @@
   dict.SetString(config_keys::kClientId, settings_.client_id);
   dict.SetString(config_keys::kClientSecret, settings_.client_secret);
   dict.SetString(config_keys::kApiKey, settings_.api_key);
-  dict.SetString(config_keys::kOAuthURL, settings_.oauth_url);
-  dict.SetString(config_keys::kServiceURL, settings_.service_url);
+  dict.SetString(config_keys::kOAuthUrl, settings_.oauth_url);
+  dict.SetString(config_keys::kServiceUrl, settings_.service_url);
   dict.SetString(config_keys::kXmppEndpoint, settings_.xmpp_endpoint);
   dict.SetString(config_keys::kRefreshToken, settings_.refresh_token);
   dict.SetString(config_keys::kCloudId, settings_.cloud_id);
diff --git a/src/config.h b/src/config.h
index 8e0a8f3..692d9c5 100644
--- a/src/config.h
+++ b/src/config.h
@@ -45,6 +45,7 @@
 
   void AddOnChangedCallback(const OnChangedCallback& callback);
   const Config::Settings& GetSettings() const;
+  const Config::Settings& GetDefaults() const;
 
   // Allows editing of config. Makes sure that callbacks were called and changes
   // were saved.
@@ -121,9 +122,9 @@
   };
 
  private:
-  void Load();
   void Save();
 
+  const Settings defaults_;
   Settings settings_;
   provider::ConfigStore* config_store_{nullptr};
   std::vector<OnChangedCallback> on_changed_;
diff --git a/src/device_manager.cc b/src/device_manager.cc
index deb5404..4c0d3ee 100644
--- a/src/device_manager.cc
+++ b/src/device_manager.cc
@@ -262,9 +262,9 @@
   return component_manager_->GetLegacyState();
 }
 
-void DeviceManager::Register(const std::string& ticket_id,
+void DeviceManager::Register(const RegistrationData& registration_data,
                              const DoneCallback& callback) {
-  device_info_->RegisterDevice(ticket_id, callback);
+  device_info_->RegisterDevice(registration_data, callback);
 }
 
 void DeviceManager::AddPairingChangedCallbacks(
diff --git a/src/device_manager.h b/src/device_manager.h
index d77bacc..f0ad464 100644
--- a/src/device_manager.h
+++ b/src/device_manager.h
@@ -69,7 +69,7 @@
                   ErrorPtr* error) override;
   Command* FindCommand(const std::string& id) override;
   void AddStateChangedCallback(const base::Closure& callback) override;
-  void Register(const std::string& ticket_id,
+  void Register(const RegistrationData& registration_data,
                 const DoneCallback& callback) override;
   GcdState GetGcdState() const override;
   void AddGcdStateChangedCallback(
diff --git a/src/device_registration_info.cc b/src/device_registration_info.cc
index 0dc1f54..3ae1321 100644
--- a/src/device_registration_info.cc
+++ b/src/device_registration_info.cc
@@ -28,6 +28,7 @@
 #include "src/json_error_codes.h"
 #include "src/notification/xmpp_channel.h"
 #include "src/privet/auth_manager.h"
+#include "src/privet/constants.h"
 #include "src/string_utils.h"
 #include "src/utils.h"
 
@@ -90,7 +91,7 @@
   return url + '?' + WebParamsEncode(params);
 }
 
-std::string BuildURL(const std::string& url,
+std::string BuildUrl(const std::string& url,
                      const std::string& subpath,
                      const WebParamList& params) {
   std::string result = url;
@@ -271,24 +272,24 @@
 
 DeviceRegistrationInfo::~DeviceRegistrationInfo() = default;
 
-std::string DeviceRegistrationInfo::GetServiceURL(
+std::string DeviceRegistrationInfo::GetServiceUrl(
     const std::string& subpath,
     const WebParamList& params) const {
-  return BuildURL(GetSettings().service_url, subpath, params);
+  return BuildUrl(GetSettings().service_url, subpath, params);
 }
 
-std::string DeviceRegistrationInfo::GetDeviceURL(
+std::string DeviceRegistrationInfo::GetDeviceUrl(
     const std::string& subpath,
     const WebParamList& params) const {
   CHECK(!GetSettings().cloud_id.empty()) << "Must have a valid device ID";
-  return BuildURL(GetSettings().service_url,
-                  "devices/" + GetSettings().cloud_id + "/" + subpath, params);
+  return GetServiceUrl("devices/" + GetSettings().cloud_id + "/" + subpath,
+                       params);
 }
 
-std::string DeviceRegistrationInfo::GetOAuthURL(
+std::string DeviceRegistrationInfo::GetOAuthUrl(
     const std::string& subpath,
     const WebParamList& params) const {
-  return BuildURL(GetSettings().oauth_url, subpath, params);
+  return BuildUrl(GetSettings().oauth_url, subpath, params);
 }
 
 void DeviceRegistrationInfo::Start() {
@@ -375,7 +376,7 @@
     return;
   }
 
-  RequestSender sender{HttpClient::Method::kPost, GetOAuthURL("token"),
+  RequestSender sender{HttpClient::Method::kPost, GetOAuthUrl("token"),
                        http_client_};
   sender.SetFormData({
       {"refresh_token", GetSettings().refresh_token},
@@ -506,7 +507,7 @@
   ErrorPtr error;
   if (!VerifyRegistrationCredentials(&error))
     return callback.Run({}, std::move(error));
-  DoCloudRequest(HttpClient::Method::kGet, GetDeviceURL(), nullptr, callback);
+  DoCloudRequest(HttpClient::Method::kGet, GetDeviceUrl(), nullptr, callback);
 }
 
 void DeviceRegistrationInfo::RegisterDeviceError(const DoneCallback& callback,
@@ -515,8 +516,47 @@
                                 base::Bind(callback, base::Passed(&error)), {});
 }
 
-void DeviceRegistrationInfo::RegisterDevice(const std::string& ticket_id,
+void DeviceRegistrationInfo::RegisterDevice(RegistrationData registration_data,
                                             const DoneCallback& callback) {
+  if (!GetSettings().allow_endpoints_override &&
+      registration_data != RegistrationData{registration_data.ticket_id}) {
+    ErrorPtr error;
+    Error::AddTo(&error, FROM_HERE, privet::errors::kInvalidParams,
+                 "Endpoint change is not permitted");
+    return RegisterDeviceError(callback, std::move(error));
+  }
+
+  // Reset OAuth to defaults, if device was unregistered values can be
+  // customized. These muse be replaced all together.
+  if (registration_data.oauth_url.empty() ||
+      registration_data.client_id.empty() ||
+      registration_data.client_secret.empty() ||
+      registration_data.api_key.empty()) {
+    registration_data.oauth_url = GetDefaults().oauth_url;
+    registration_data.client_id = GetDefaults().client_id;
+    registration_data.client_secret = GetDefaults().client_secret;
+    registration_data.api_key = GetDefaults().api_key;
+  }
+
+  // Reset Server URL to default, if device was unregistered value can be
+  // customized.
+  if (registration_data.service_url.empty())
+    registration_data.service_url = GetDefaults().service_url;
+
+  // Reset XMPP to default, if device was unregistered value can be
+  // customized.
+  if (registration_data.xmpp_endpoint.empty())
+    registration_data.xmpp_endpoint = GetDefaults().xmpp_endpoint;
+
+  VLOG(1) << "RegisterDevice: "
+          << "ticket_id: " << registration_data.ticket_id
+          << ", oauth_url: " << registration_data.oauth_url
+          << ", client_id: " << registration_data.client_id
+          << ", client_secret: " << registration_data.client_secret
+          << ", api_key: " << registration_data.api_key
+          << ", service_url: " << registration_data.service_url
+          << ", xmpp_endpoint: " << registration_data.xmpp_endpoint;
+
   if (HaveRegistrationCredentials()) {
     ErrorPtr error;
     Error::AddTo(&error, FROM_HERE, kErrorAlreayRegistered,
@@ -528,21 +568,23 @@
   CHECK(device_draft);
 
   base::DictionaryValue req_json;
-  req_json.SetString("id", ticket_id);
-  req_json.SetString("oauthClientId", GetSettings().client_id);
+  req_json.SetString("id", registration_data.ticket_id);
+  req_json.SetString("oauthClientId", registration_data.client_id);
   req_json.Set("deviceDraft", device_draft.release());
 
-  auto url = GetServiceURL("registrationTickets/" + ticket_id,
-                           {{"key", GetSettings().api_key}});
+  auto url = BuildUrl(registration_data.service_url,
+                      "registrationTickets/" + registration_data.ticket_id,
+                      {{"key", registration_data.api_key}});
 
   RequestSender sender{HttpClient::Method::kPatch, url, http_client_};
   sender.SetJsonData(req_json);
   sender.Send(base::Bind(&DeviceRegistrationInfo::RegisterDeviceOnTicketSent,
-                         weak_factory_.GetWeakPtr(), ticket_id, callback));
+                         weak_factory_.GetWeakPtr(), registration_data,
+                         callback));
 }
 
 void DeviceRegistrationInfo::RegisterDeviceOnTicketSent(
-    const std::string& ticket_id,
+    const RegistrationData& registration_data,
     const DoneCallback& callback,
     std::unique_ptr<provider::HttpClient::Response> response,
     ErrorPtr error) {
@@ -557,15 +599,17 @@
     return RegisterDeviceError(callback, std::move(error));
   }
 
-  std::string url =
-      GetServiceURL("registrationTickets/" + ticket_id + "/finalize",
-                    {{"key", GetSettings().api_key}});
+  std::string url = BuildUrl(
+      registration_data.service_url,
+      "registrationTickets/" + registration_data.ticket_id + "/finalize",
+      {{"key", registration_data.api_key}});
   RequestSender{HttpClient::Method::kPost, url, http_client_}.Send(
       base::Bind(&DeviceRegistrationInfo::RegisterDeviceOnTicketFinalized,
-                 weak_factory_.GetWeakPtr(), callback));
+                 weak_factory_.GetWeakPtr(), registration_data, callback));
 }
 
 void DeviceRegistrationInfo::RegisterDeviceOnTicketFinalized(
+    const RegistrationData& registration_data,
     const DoneCallback& callback,
     std::unique_ptr<provider::HttpClient::Response> response,
     ErrorPtr error) {
@@ -595,19 +639,21 @@
   UpdateDeviceInfoTimestamp(*device_draft_response);
 
   // Now get access_token and refresh_token
-  RequestSender sender2{HttpClient::Method::kPost, GetOAuthURL("token"),
+  RequestSender sender2{HttpClient::Method::kPost,
+                        BuildUrl(registration_data.oauth_url, "token", {}),
                         http_client_};
   sender2.SetFormData({{"code", auth_code},
-                       {"client_id", GetSettings().client_id},
-                       {"client_secret", GetSettings().client_secret},
+                       {"client_id", registration_data.client_id},
+                       {"client_secret", registration_data.client_secret},
                        {"redirect_uri", "oob"},
                        {"grant_type", "authorization_code"}});
   sender2.Send(base::Bind(&DeviceRegistrationInfo::RegisterDeviceOnAuthCodeSent,
-                          weak_factory_.GetWeakPtr(), cloud_id, robot_account,
-                          callback));
+                          weak_factory_.GetWeakPtr(), registration_data,
+                          cloud_id, robot_account, callback));
 }
 
 void DeviceRegistrationInfo::RegisterDeviceOnAuthCodeSent(
+    const RegistrationData& registration_data,
     const std::string& cloud_id,
     const std::string& robot_account,
     const DoneCallback& callback,
@@ -631,9 +677,18 @@
       base::Time::Now() + base::TimeDelta::FromSeconds(expires_in);
 
   Config::Transaction change{config_};
+
   change.set_cloud_id(cloud_id);
   change.set_robot_account(robot_account);
   change.set_refresh_token(refresh_token);
+
+  change.set_oauth_url(registration_data.oauth_url);
+  change.set_client_id(registration_data.client_id);
+  change.set_client_secret(registration_data.client_secret);
+  change.set_api_key(registration_data.api_key);
+  change.set_service_url(registration_data.service_url);
+  change.set_xmpp_endpoint(registration_data.xmpp_endpoint);
+
   change.Commit();
 
   task_runner_->PostDelayedTask(FROM_HERE, base::Bind(callback, nullptr), {});
@@ -828,40 +883,12 @@
   change.set_local_pairing_enabled(local_pairing_enabled);
 }
 
-bool DeviceRegistrationInfo::UpdateServiceConfig(
-    const std::string& client_id,
-    const std::string& client_secret,
-    const std::string& api_key,
-    const std::string& oauth_url,
-    const std::string& service_url,
-    const std::string& xmpp_endpoint,
-    ErrorPtr* error) {
-  if (HaveRegistrationCredentials()) {
-    return Error::AddTo(error, FROM_HERE, kErrorAlreayRegistered,
-                        "Unable to change config for registered device");
-  }
-  Config::Transaction change{config_};
-  if (!client_id.empty())
-    change.set_client_id(client_id);
-  if (!client_secret.empty())
-    change.set_client_secret(client_secret);
-  if (!api_key.empty())
-    change.set_api_key(api_key);
-  if (!oauth_url.empty())
-    change.set_oauth_url(oauth_url);
-  if (!service_url.empty())
-    change.set_service_url(service_url);
-  if (!xmpp_endpoint.empty())
-    change.set_xmpp_endpoint(xmpp_endpoint);
-  return true;
-}
-
 void DeviceRegistrationInfo::UpdateCommand(
     const std::string& command_id,
     const base::DictionaryValue& command_patch,
     const DoneCallback& callback) {
   DoCloudRequest(HttpClient::Method::kPatch,
-                 GetServiceURL("commands/" + command_id), &command_patch,
+                 GetServiceUrl("commands/" + command_id), &command_patch,
                  base::Bind(&IgnoreCloudResultWithCallback, callback));
 }
 
@@ -915,7 +942,7 @@
       BuildDeviceResource();
   CHECK(device_resource);
 
-  std::string url = GetDeviceURL(
+  std::string url = GetDeviceUrl(
       {}, {{"lastUpdateTimeMs", last_device_resource_updated_timestamp_}});
 
   DoCloudRequest(HttpClient::Method::kPut, url, device_resource.get(),
@@ -951,7 +978,7 @@
   std::unique_ptr<base::DictionaryValue> root{new base::DictionaryValue};
   root->Set("localAuthInfo", auth.release());
 
-  std::string url = GetDeviceURL("upsertLocalAuthInfo", {});
+  std::string url = GetDeviceUrl("upsertLocalAuthInfo", {});
   DoCloudRequest(HttpClient::Method::kPost, url, root.get(),
                  base::Bind(&DeviceRegistrationInfo::OnSendAuthInfoDone,
                             AsWeakPtr(), token));
@@ -1058,7 +1085,7 @@
   fetch_commands_request_queued_ = false;
   DoCloudRequest(
       HttpClient::Method::kGet,
-      GetServiceURL("commands/queue",
+      GetServiceUrl("commands/queue",
                     {{"deviceId", GetSettings().cloud_id}, {"reason", reason}}),
       nullptr, base::Bind(&DeviceRegistrationInfo::OnFetchCommandsDone,
                           AsWeakPtr(), callback));
@@ -1106,7 +1133,7 @@
       cmd_copy->SetString("state", "aborted");
       // TODO(wiley) We could consider handling this error case more gracefully.
       DoCloudRequest(HttpClient::Method::kPut,
-                     GetServiceURL("commands/" + command_id), cmd_copy.get(),
+                     GetServiceUrl("commands/" + command_id), cmd_copy.get(),
                      base::Bind(&IgnoreCloudResult));
     } else {
       // Normal command, publish it to local clients.
@@ -1184,7 +1211,7 @@
   body.Set("patches", patches.release());
 
   device_state_update_pending_ = true;
-  DoCloudRequest(HttpClient::Method::kPost, GetDeviceURL("patchState"), &body,
+  DoCloudRequest(HttpClient::Method::kPost, GetDeviceUrl("patchState"), &body,
                  base::Bind(&DeviceRegistrationInfo::OnPublishStateDone,
                             AsWeakPtr(), snapshot.update_id));
 }
diff --git a/src/device_registration_info.h b/src/device_registration_info.h
index a296258..a488bae 100644
--- a/src/device_registration_info.h
+++ b/src/device_registration_info.h
@@ -64,7 +64,8 @@
 
   void AddGcdStateChangedCallback(
       const Device::GcdStateChangedCallback& callback);
-  void RegisterDevice(const std::string& ticket_id,
+
+  void RegisterDevice(RegistrationData registration_data,
                       const DoneCallback& callback);
 
   void UpdateDeviceInfo(const std::string& name,
@@ -73,13 +74,6 @@
   void UpdateBaseConfig(AuthScope anonymous_access_role,
                         bool local_discovery_enabled,
                         bool local_pairing_enabled);
-  bool UpdateServiceConfig(const std::string& client_id,
-                           const std::string& client_secret,
-                           const std::string& api_key,
-                           const std::string& oauth_url,
-                           const std::string& service_url,
-                           const std::string& xmpp_endpoint,
-                           ErrorPtr* error);
 
   void GetDeviceInfo(const CloudRequestDoneCallback& callback);
 
@@ -90,21 +84,21 @@
   // WebParamsEncode() and appended to URL as a query
   // string.
   // So, calling:
-  //    GetServiceURL("ticket", {{"key","apiKey"}})
+  //    GetServiceUrl("ticket", {{"key","apiKey"}})
   // will return something like:
   //    https://www.googleapis.com/weave/v1/ticket?key=apiKey
-  std::string GetServiceURL(const std::string& subpath = {},
+  std::string GetServiceUrl(const std::string& subpath = {},
                             const WebParamList& params = {}) const;
 
   // Returns a service URL to access the registered device on GCD server.
   // The base URL used to construct the full URL looks like this:
   //    https://www.googleapis.com/weave/v1/devices/<cloud_id>/
-  std::string GetDeviceURL(const std::string& subpath = {},
+  std::string GetDeviceUrl(const std::string& subpath = {},
                            const WebParamList& params = {}) const;
 
   // Similar to GetServiceURL, GetOAuthURL() returns a URL of OAuth 2.0 server.
   // The base URL used is https://accounts.google.com/o/oauth2/.
-  std::string GetOAuthURL(const std::string& subpath = {},
+  std::string GetOAuthUrl(const std::string& subpath = {},
                           const WebParamList& params = {}) const;
 
   // Starts GCD device if credentials available.
@@ -124,6 +118,8 @@
  private:
   friend class DeviceRegistrationInfoTest;
 
+  const Config::Settings& GetDefaults() const { return config_->GetDefaults(); }
+
   base::WeakPtr<DeviceRegistrationInfo> AsWeakPtr() {
     return weak_factory_.GetWeakPtr();
   }
@@ -276,15 +272,17 @@
 
   void RegisterDeviceError(const DoneCallback& callback, ErrorPtr error);
   void RegisterDeviceOnTicketSent(
-      const std::string& ticket_id,
+      const RegistrationData& registration_data,
       const DoneCallback& callback,
       std::unique_ptr<provider::HttpClient::Response> response,
       ErrorPtr error);
   void RegisterDeviceOnTicketFinalized(
+      const RegistrationData& registration_data,
       const DoneCallback& callback,
       std::unique_ptr<provider::HttpClient::Response> response,
       ErrorPtr error);
   void RegisterDeviceOnAuthCodeSent(
+      const RegistrationData& registration_data,
       const std::string& cloud_id,
       const std::string& robot_account,
       const DoneCallback& callback,
diff --git a/src/device_registration_info_unittest.cc b/src/device_registration_info_unittest.cc
index bbc167e..adc5175 100644
--- a/src/device_registration_info_unittest.cc
+++ b/src/device_registration_info_unittest.cc
@@ -45,8 +45,8 @@
 namespace test_data {
 
 const char kXmppEndpoint[] = "xmpp.server.com:1234";
-const char kServiceURL[] = "http://gcd.server.com/";
-const char kOAuthURL[] = "http://oauth.server.com/";
+const char kServiceUrl[] = "http://gcd.server.com/";
+const char kOAuthUrl[] = "http://oauth.server.com/";
 const char kApiKey[] = "GOadRdTf9FERf0k4w6EFOof56fUJ3kFDdFL3d7f";
 const char kClientId[] =
     "123543821385-sfjkjshdkjhfk234sdfsdfkskd"
@@ -127,12 +127,12 @@
   void SetUp() override {
     EXPECT_CALL(clock_, Now())
         .WillRepeatedly(Return(base::Time::FromTimeT(1450000000)));
-    ReloadDefaults();
+    ReloadDefaults(true);
   }
 
-  void ReloadDefaults() {
+  void ReloadDefaults(bool allow_endpoints_override) {
     EXPECT_CALL(config_store_, LoadDefaults(_))
-        .WillOnce(Invoke([](Settings* settings) {
+        .WillOnce(Invoke([allow_endpoints_override](Settings* settings) {
           settings->client_id = test_data::kClientId;
           settings->client_secret = test_data::kClientSecret;
           settings->api_key = test_data::kApiKey;
@@ -143,9 +143,10 @@
           settings->location = "Kitchen";
           settings->local_anonymous_access_role = AuthScope::kViewer;
           settings->model_id = "AAAAA";
-          settings->oauth_url = test_data::kOAuthURL;
-          settings->service_url = test_data::kServiceURL;
+          settings->oauth_url = test_data::kOAuthUrl;
+          settings->service_url = test_data::kServiceUrl;
           settings->xmpp_endpoint = test_data::kXmppEndpoint;
+          settings->allow_endpoints_override = allow_endpoints_override;
           return true;
         }));
     config_.reset(new Config{&config_store_});
@@ -155,7 +156,7 @@
     dev_reg_->Start();
   }
 
-  void ReloadSettings(bool registered = true) {
+  void ReloadSettings(bool registered, bool allow_endpoints_override) {
     base::DictionaryValue dict;
     dict.SetInteger("version", 1);
     if (registered) {
@@ -168,7 +169,7 @@
     base::JSONWriter::WriteWithOptions(
         dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_string);
     EXPECT_CALL(config_store_, LoadSettings()).WillOnce(Return(json_string));
-    ReloadDefaults();
+    ReloadDefaults(allow_endpoints_override);
   }
 
   void PublishCommands(const base::ListValue& commands) {
@@ -196,6 +197,9 @@
     return dev_reg_->HaveRegistrationCredentials();
   }
 
+  void RegisterDevice(const RegistrationData registration_data,
+                      const RegistrationData& expected_data);
+
   provider::test::FakeTaskRunner task_runner_;
   provider::test::MockConfigStore config_store_;
   StrictMock<MockHttpClient> http_client_;
@@ -213,31 +217,31 @@
   ComponentManagerImpl component_manager_{&task_runner_};
 };
 
-TEST_F(DeviceRegistrationInfoTest, GetServiceURL) {
-  EXPECT_EQ(test_data::kServiceURL, dev_reg_->GetServiceURL());
-  std::string url = test_data::kServiceURL;
+TEST_F(DeviceRegistrationInfoTest, GetServiceUrl) {
+  EXPECT_EQ(test_data::kServiceUrl, dev_reg_->GetServiceUrl());
+  std::string url = test_data::kServiceUrl;
   url += "registrationTickets";
-  EXPECT_EQ(url, dev_reg_->GetServiceURL("registrationTickets"));
+  EXPECT_EQ(url, dev_reg_->GetServiceUrl("registrationTickets"));
   url += "?key=";
   url += test_data::kApiKey;
-  EXPECT_EQ(url, dev_reg_->GetServiceURL("registrationTickets",
+  EXPECT_EQ(url, dev_reg_->GetServiceUrl("registrationTickets",
                                          {{"key", test_data::kApiKey}}));
   url += "&restart=true";
-  EXPECT_EQ(url, dev_reg_->GetServiceURL(
+  EXPECT_EQ(url, dev_reg_->GetServiceUrl(
                      "registrationTickets",
                      {
                          {"key", test_data::kApiKey}, {"restart", "true"},
                      }));
 }
 
-TEST_F(DeviceRegistrationInfoTest, GetOAuthURL) {
-  EXPECT_EQ(test_data::kOAuthURL, dev_reg_->GetOAuthURL());
-  std::string url = test_data::kOAuthURL;
+TEST_F(DeviceRegistrationInfoTest, GetOAuthUrl) {
+  EXPECT_EQ(test_data::kOAuthUrl, dev_reg_->GetOAuthUrl());
+  std::string url = test_data::kOAuthUrl;
   url += "auth?redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&";
   url += "response_type=code&";
   url += "client_id=";
   url += test_data::kClientId;
-  EXPECT_EQ(url, dev_reg_->GetOAuthURL(
+  EXPECT_EQ(url, dev_reg_->GetOAuthUrl(
                      "auth", {{"redirect_uri", "urn:ietf:wg:oauth:2.0:oob"},
                               {"response_type", "code"},
                               {"client_id", test_data::kClientId}}));
@@ -245,11 +249,11 @@
 
 TEST_F(DeviceRegistrationInfoTest, HaveRegistrationCredentials) {
   EXPECT_FALSE(HaveRegistrationCredentials());
-  ReloadSettings();
+  ReloadSettings(true, false);
 
   EXPECT_CALL(
       http_client_,
-      SendRequest(HttpClient::Method::kPost, dev_reg_->GetOAuthURL("token"),
+      SendRequest(HttpClient::Method::kPost, dev_reg_->GetOAuthUrl("token"),
                   HttpClient::Headers{GetFormHeader()}, _, _))
       .WillOnce(WithArgs<3, 4>(
           Invoke([](const std::string& data,
@@ -287,12 +291,12 @@
 }
 
 TEST_F(DeviceRegistrationInfoTest, CheckAuthenticationFailure) {
-  ReloadSettings();
+  ReloadSettings(true, false);
   EXPECT_EQ(GcdState::kConnecting, GetGcdState());
 
   EXPECT_CALL(
       http_client_,
-      SendRequest(HttpClient::Method::kPost, dev_reg_->GetOAuthURL("token"),
+      SendRequest(HttpClient::Method::kPost, dev_reg_->GetOAuthUrl("token"),
                   HttpClient::Headers{GetFormHeader()}, _, _))
       .WillOnce(WithArgs<3, 4>(
           Invoke([](const std::string& data,
@@ -316,12 +320,12 @@
 }
 
 TEST_F(DeviceRegistrationInfoTest, CheckDeregistration) {
-  ReloadSettings();
+  ReloadSettings(true, false);
   EXPECT_EQ(GcdState::kConnecting, GetGcdState());
 
   EXPECT_CALL(
       http_client_,
-      SendRequest(HttpClient::Method::kPost, dev_reg_->GetOAuthURL("token"),
+      SendRequest(HttpClient::Method::kPost, dev_reg_->GetOAuthUrl("token"),
                   HttpClient::Headers{GetFormHeader()}, _, _))
       .WillOnce(WithArgs<3, 4>(
           Invoke([](const std::string& data,
@@ -346,12 +350,12 @@
 }
 
 TEST_F(DeviceRegistrationInfoTest, GetDeviceInfo) {
-  ReloadSettings();
+  ReloadSettings(true, false);
   SetAccessToken();
 
   EXPECT_CALL(
       http_client_,
-      SendRequest(HttpClient::Method::kGet, dev_reg_->GetDeviceURL(),
+      SendRequest(HttpClient::Method::kGet, dev_reg_->GetDeviceUrl(),
                   HttpClient::Headers{GetAuthHeader(), GetJsonHeader()}, _, _))
       .WillOnce(WithArgs<3, 4>(
           Invoke([](const std::string& data,
@@ -377,9 +381,32 @@
   EXPECT_TRUE(succeeded);
 }
 
-TEST_F(DeviceRegistrationInfoTest, RegisterDevice) {
-  ReloadSettings(false);
+TEST_F(DeviceRegistrationInfoTest, ReRegisterDevice) {
+  ReloadSettings(true, false);
 
+  bool done = false;
+  dev_reg_->RegisterDevice(RegistrationData{test_data::kClaimTicketId},
+                           base::Bind([this, &done](ErrorPtr error) {
+                             EXPECT_TRUE(error->HasError("already_registered"));
+                             done = true;
+                             task_runner_.Break();
+                             EXPECT_EQ(GcdState::kConnecting, GetGcdState());
+
+                             // Validate the device info saved to storage...
+                             EXPECT_EQ(test_data::kCloudId,
+                                       dev_reg_->GetSettings().cloud_id);
+                             EXPECT_EQ(test_data::kRefreshToken,
+                                       dev_reg_->GetSettings().refresh_token);
+                             EXPECT_EQ(test_data::kRobotAccountEmail,
+                                       dev_reg_->GetSettings().robot_account);
+                           }));
+  task_runner_.Run();
+  EXPECT_TRUE(done);
+}
+
+void DeviceRegistrationInfoTest::RegisterDevice(
+    const RegistrationData registration_data,
+    const RegistrationData& expected_data) {
   auto json_traits = CreateDictionaryValue(R"({
     'base': {
       'commands': {
@@ -408,25 +435,25 @@
   EXPECT_TRUE(component_manager_.SetStateProperty(
       "comp", "base.firmwareVersion", ver, nullptr));
 
-  std::string ticket_url = dev_reg_->GetServiceURL("registrationTickets/") +
-                           test_data::kClaimTicketId;
+  std::string ticket_url = expected_data.service_url + "registrationTickets/" +
+                           expected_data.ticket_id;
   EXPECT_CALL(http_client_,
               SendRequest(HttpClient::Method::kPatch,
-                          ticket_url + "?key=" + test_data::kApiKey,
+                          ticket_url + "?key=" + expected_data.api_key,
                           HttpClient::Headers{GetJsonHeader()}, _, _))
-      .WillOnce(WithArgs<3, 4>(
-          Invoke([](const std::string& data,
-                    const HttpClient::SendRequestCallback& callback) {
+      .WillOnce(WithArgs<3, 4>(Invoke(
+          [&expected_data](const std::string& data,
+                           const HttpClient::SendRequestCallback& callback) {
             auto json = test::CreateDictionaryValue(data);
             EXPECT_NE(nullptr, json.get());
             std::string value;
             EXPECT_TRUE(json->GetString("id", &value));
-            EXPECT_EQ(test_data::kClaimTicketId, value);
+            EXPECT_EQ(expected_data.ticket_id, value);
             EXPECT_TRUE(
                 json->GetString("deviceDraft.channel.supportedType", &value));
             EXPECT_EQ("pull", value);
             EXPECT_TRUE(json->GetString("oauthClientId", &value));
-            EXPECT_EQ(test_data::kClientId, value);
+            EXPECT_EQ(expected_data.client_id, value);
             EXPECT_TRUE(json->GetString("deviceDraft.description", &value));
             EXPECT_EQ("Easy to clean", value);
             EXPECT_TRUE(json->GetString("deviceDraft.location", &value));
@@ -489,7 +516,7 @@
 
   EXPECT_CALL(http_client_,
               SendRequest(HttpClient::Method::kPost,
-                          ticket_url + "/finalize?key=" + test_data::kApiKey,
+                          ticket_url + "/finalize?key=" + expected_data.api_key,
                           HttpClient::Headers{}, _, _))
       .WillOnce(WithArgs<4>(
           Invoke([](const HttpClient::SendRequestCallback& callback) {
@@ -509,15 +536,15 @@
 
   EXPECT_CALL(
       http_client_,
-      SendRequest(HttpClient::Method::kPost, dev_reg_->GetOAuthURL("token"),
+      SendRequest(HttpClient::Method::kPost, expected_data.oauth_url + "token",
                   HttpClient::Headers{GetFormHeader()}, _, _))
-      .WillOnce(WithArgs<3, 4>(Invoke([](
+      .WillOnce(WithArgs<3, 4>(Invoke([&expected_data](
           const std::string& data,
           const HttpClient::SendRequestCallback& callback) {
         EXPECT_EQ("authorization_code", GetFormField(data, "grant_type"));
         EXPECT_EQ(test_data::kRobotAccountAuthCode, GetFormField(data, "code"));
-        EXPECT_EQ(test_data::kClientId, GetFormField(data, "client_id"));
-        EXPECT_EQ(test_data::kClientSecret,
+        EXPECT_EQ(expected_data.client_id, GetFormField(data, "client_id"));
+        EXPECT_EQ(expected_data.client_secret,
                   GetFormField(data, "client_secret"));
         EXPECT_EQ("oob", GetFormField(data, "redirect_uri"));
 
@@ -532,7 +559,9 @@
 
   EXPECT_CALL(
       http_client_,
-      SendRequest(HttpClient::Method::kPost, HasSubstr("upsertLocalAuthInfo"),
+      SendRequest(HttpClient::Method::kPost,
+                  expected_data.service_url + "devices/" + test_data::kCloudId +
+                      "/upsertLocalAuthInfo",
                   HttpClient::Headers{GetAuthHeader(), GetJsonHeader()}, _, _))
       .WillOnce(WithArgs<3, 4>(
           Invoke([](const std::string& data,
@@ -546,10 +575,12 @@
 
   bool done = false;
   dev_reg_->RegisterDevice(
-      test_data::kClaimTicketId, base::Bind([this, &done](ErrorPtr error) {
-        EXPECT_FALSE(error);
+      registration_data,
+      base::Bind([this, &done, &expected_data](ErrorPtr error) {
         done = true;
         task_runner_.Break();
+
+        EXPECT_FALSE(error);
         EXPECT_EQ(GcdState::kConnecting, GetGcdState());
 
         // Validate the device info saved to storage...
@@ -558,29 +589,66 @@
                   dev_reg_->GetSettings().refresh_token);
         EXPECT_EQ(test_data::kRobotAccountEmail,
                   dev_reg_->GetSettings().robot_account);
+        EXPECT_EQ(expected_data.oauth_url, dev_reg_->GetSettings().oauth_url);
+        EXPECT_EQ(expected_data.client_id, dev_reg_->GetSettings().client_id);
+        EXPECT_EQ(expected_data.client_secret,
+                  dev_reg_->GetSettings().client_secret);
+        EXPECT_EQ(expected_data.api_key, dev_reg_->GetSettings().api_key);
+        EXPECT_EQ(expected_data.service_url,
+                  dev_reg_->GetSettings().service_url);
+        EXPECT_EQ(expected_data.xmpp_endpoint,
+                  dev_reg_->GetSettings().xmpp_endpoint);
       }));
   task_runner_.Run();
   EXPECT_TRUE(done);
 }
 
-TEST_F(DeviceRegistrationInfoTest, ReRegisterDevice) {
-  ReloadSettings();
+TEST_F(DeviceRegistrationInfoTest, RegisterDevice) {
+  ReloadSettings(false, true);
+
+  RegistrationData registration_data;
+  registration_data.ticket_id = "test_ticked_id";
+  registration_data.oauth_url = "https://test.oauth/";
+  registration_data.client_id = "test_client_id";
+  registration_data.client_secret = "test_client_secret";
+  registration_data.api_key = "test_api_key";
+  registration_data.service_url = "https://test.service/";
+  registration_data.xmpp_endpoint = "test.xmpp:1234";
+
+  RegisterDevice(registration_data, registration_data);
+}
+
+TEST_F(DeviceRegistrationInfoTest, RegisterDeviceWithDefaultEndpoints) {
+  ReloadSettings(false, true);
+
+  RegistrationData registration_data;
+  registration_data.ticket_id = "test_ticked_id";
+
+  RegistrationData expected_data = registration_data;
+  expected_data.oauth_url = test_data::kOAuthUrl;
+  expected_data.client_id = test_data::kClientId;
+  expected_data.client_secret = test_data::kClientSecret;
+  expected_data.api_key = test_data::kApiKey;
+  expected_data.service_url = test_data::kServiceUrl;
+  expected_data.xmpp_endpoint = test_data::kXmppEndpoint;
+
+  RegisterDevice(registration_data, expected_data);
+}
+
+TEST_F(DeviceRegistrationInfoTest, RegisterDeviceEndpointsOverrideNotAllowed) {
+  ReloadSettings(false, false);
+
+  RegistrationData registration_data;
+  registration_data.ticket_id = "test_ticked_id";
+  registration_data.service_url = "https://test.service/";
 
   bool done = false;
-  dev_reg_->RegisterDevice(
-      test_data::kClaimTicketId, base::Bind([this, &done](ErrorPtr error) {
-        EXPECT_TRUE(error->HasError("already_registered"));
-        done = true;
-        task_runner_.Break();
-        EXPECT_EQ(GcdState::kConnecting, GetGcdState());
-
-        // Validate the device info saved to storage...
-        EXPECT_EQ(test_data::kCloudId, dev_reg_->GetSettings().cloud_id);
-        EXPECT_EQ(test_data::kRefreshToken,
-                  dev_reg_->GetSettings().refresh_token);
-        EXPECT_EQ(test_data::kRobotAccountEmail,
-                  dev_reg_->GetSettings().robot_account);
-      }));
+  dev_reg_->RegisterDevice(registration_data,
+                           base::Bind([this, &done](ErrorPtr error) {
+                             done = true;
+                             task_runner_.Break();
+                             EXPECT_TRUE(error->HasError("invalidParams"));
+                           }));
   task_runner_.Run();
   EXPECT_TRUE(done);
 }
@@ -590,7 +658,7 @@
   // unregistered, depending on whether or not we've found credentials.
   EXPECT_EQ(GcdState::kUnconfigured, GetGcdState());
   // Put some credentials into our state, make sure we call that offline.
-  ReloadSettings();
+  ReloadSettings(true, false);
   EXPECT_EQ(GcdState::kConnecting, GetGcdState());
 }
 
@@ -600,7 +668,7 @@
   void SetUp() override {
     DeviceRegistrationInfoTest::SetUp();
 
-    ReloadSettings();
+    ReloadSettings(true, false);
     SetAccessToken();
 
     auto json_traits = CreateDictionaryValue(R"({
@@ -619,7 +687,7 @@
     EXPECT_TRUE(
         component_manager_.AddComponent("", "comp", {"robot"}, nullptr));
 
-    command_url_ = dev_reg_->GetServiceURL("commands/1234");
+    command_url_ = dev_reg_->GetServiceUrl("commands/1234");
 
     auto commands_json = CreateValue(R"([{
       'name':'robot._jump',
diff --git a/src/privet/cloud_delegate.cc b/src/privet/cloud_delegate.cc
index 49fceaa..0b4400f 100644
--- a/src/privet/cloud_delegate.cc
+++ b/src/privet/cloud_delegate.cc
@@ -12,6 +12,7 @@
 #include <base/memory/weak_ptr.h>
 #include <base/values.h>
 #include <weave/error.h>
+#include <weave/device.h>
 #include <weave/provider/task_runner.h>
 
 #include "src/backoff_entry.h"
@@ -109,15 +110,13 @@
 
   const SetupState& GetSetupState() const override { return setup_state_; }
 
-  bool Setup(const std::string& ticket_id,
-             const std::string& user,
+  bool Setup(const RegistrationData& registration_data,
              ErrorPtr* error) override {
-    VLOG(1) << "GCD Setup started. ticket_id: " << ticket_id
-            << ", user:" << user;
+    VLOG(1) << "GCD Setup started. ";
     // Set (or reset) the retry counter, since we are starting a new
     // registration process.
     registation_retry_count_ = kMaxDeviceRegistrationRetries;
-    ticket_id_ = ticket_id;
+    registration_data_ = registration_data;
     if (setup_state_.IsStatusEqual(SetupState::kInProgress)) {
       // Another registration is in progress. In case it fails, we will use
       // the new ticket ID when retrying the request.
@@ -140,6 +139,18 @@
                : "";
   }
 
+  std::string GetOAuthUrl() const override {
+    return device_->GetSettings().oauth_url;
+  }
+
+  std::string GetServiceUrl() const override {
+    return device_->GetSettings().service_url;
+  }
+
+  std::string GetXmppEndpoint() const override {
+    return device_->GetSettings().xmpp_endpoint;
+  }
+
   const base::DictionaryValue& GetLegacyCommandDef() const override {
     return component_manager_->GetLegacyCommandDefinitions();
   }
@@ -273,7 +284,7 @@
       return;
     }
 
-    device_->RegisterDevice(ticket_id_,
+    device_->RegisterDevice(registration_data_,
                             base::Bind(&CloudDelegateImpl::RegisterDeviceDone,
                                        setup_weak_factory_.GetWeakPtr()));
   }
@@ -337,8 +348,8 @@
   // State of the current or last setup.
   SetupState setup_state_{SetupState::kNone};
 
-  // Ticket ID for registering the device.
-  std::string ticket_id_;
+  // Registration data for current registration process.
+  RegistrationData registration_data_;
 
   // Number of remaining retries for device registration process.
   int registation_retry_count_{0};
diff --git a/src/privet/cloud_delegate.h b/src/privet/cloud_delegate.h
index 9f053d8..37ed723 100644
--- a/src/privet/cloud_delegate.h
+++ b/src/privet/cloud_delegate.h
@@ -12,6 +12,7 @@
 #include <base/callback.h>
 #include <base/memory/ref_counted.h>
 #include <base/observer_list.h>
+#include <weave/device.h>
 
 #include "src/privet/privet_types.h"
 #include "src/privet/security_delegate.h"
@@ -88,13 +89,17 @@
   virtual const SetupState& GetSetupState() const = 0;
 
   // Starts GCD setup.
-  virtual bool Setup(const std::string& ticket_id,
-                     const std::string& user,
+  virtual bool Setup(const RegistrationData& registration_data,
                      ErrorPtr* error) = 0;
 
   // Returns cloud id if the registered device or empty string if unregistered.
   virtual std::string GetCloudId() const = 0;
 
+  // Returns currently active cloud endponts.
+  virtual std::string GetOAuthUrl() const = 0;
+  virtual std::string GetServiceUrl() const = 0;
+  virtual std::string GetXmppEndpoint() const = 0;
+
   // Returns dictionary with device state (for legacy APIs).
   virtual const base::DictionaryValue& GetLegacyState() const = 0;
 
diff --git a/src/privet/mock_delegates.h b/src/privet/mock_delegates.h
index c2e9a89..108e450 100644
--- a/src/privet/mock_delegates.h
+++ b/src/privet/mock_delegates.h
@@ -183,8 +183,11 @@
   MOCK_CONST_METHOD0(GetAnonymousMaxScope, AuthScope());
   MOCK_CONST_METHOD0(GetConnectionState, const ConnectionState&());
   MOCK_CONST_METHOD0(GetSetupState, const SetupState&());
-  MOCK_METHOD3(Setup, bool(const std::string&, const std::string&, ErrorPtr*));
+  MOCK_METHOD2(Setup, bool(const RegistrationData&, ErrorPtr*));
   MOCK_CONST_METHOD0(GetCloudId, std::string());
+  MOCK_CONST_METHOD0(GetOAuthUrl, std::string());
+  MOCK_CONST_METHOD0(GetServiceUrl, std::string());
+  MOCK_CONST_METHOD0(GetXmppEndpoint, std::string());
   MOCK_CONST_METHOD0(GetLegacyState, const base::DictionaryValue&());
   MOCK_CONST_METHOD0(GetLegacyCommandDef, const base::DictionaryValue&());
   MOCK_CONST_METHOD0(GetComponents, const base::DictionaryValue&());
@@ -208,6 +211,10 @@
 
   MockCloudDelegate() {
     EXPECT_CALL(*this, GetDeviceId()).WillRepeatedly(Return("TestId"));
+    EXPECT_CALL(*this, GetOAuthUrl()).WillRepeatedly(Return("https://oauths/"));
+    EXPECT_CALL(*this, GetServiceUrl())
+        .WillRepeatedly(Return("https://service/"));
+    EXPECT_CALL(*this, GetXmppEndpoint()).WillRepeatedly(Return("xmpp:678"));
     EXPECT_CALL(*this, GetModelId()).WillRepeatedly(Return("ABMID"));
     EXPECT_CALL(*this, GetName()).WillRepeatedly(Return("TestDevice"));
     EXPECT_CALL(*this, GetDescription()).WillRepeatedly(Return(""));
diff --git a/src/privet/privet_handler.cc b/src/privet/privet_handler.cc
index e8b1c77..05b6e0a 100644
--- a/src/privet/privet_handler.cc
+++ b/src/privet/privet_handler.cc
@@ -14,6 +14,7 @@
 #include <base/location.h>
 #include <base/strings/stringprintf.h>
 #include <base/values.h>
+#include <weave/device.h>
 #include <weave/enum_to_string.h>
 #include <weave/provider/task_runner.h>
 
@@ -98,6 +99,19 @@
 const char kSetupStartPassKey[] = "passphrase";
 const char kSetupStartTicketIdKey[] = "ticketId";
 const char kSetupStartUserKey[] = "user";
+const char kSetupStartClientIdKey[] = "client_id";
+const char kSetupStartClientSecretKey[] = "client_secret";
+const char kSetupStartApiKeyKey[] = "api_key";
+const char kSetupStartOAuthUrlKey[] = "oauth_url";
+const char kSetupStartServiceUrlKey[] = "service_url";
+const char kSetupStartXmppEndpointKey[] = "xmpp_endpoint";
+
+std::string oauth_url;
+std::string client_id;
+std::string client_secret;
+std::string api_key;
+std::string service_url;
+std::string xmpp_endpoint;
 
 const char kFingerprintKey[] = "fingerprint";
 const char kStateKey[] = "state";
@@ -285,10 +299,17 @@
   return result;
 }
 
+void SetGcdProperties(const CloudDelegate& cloud, base::DictionaryValue* dict) {
+  dict->SetString(kInfoIdKey, cloud.GetCloudId());
+  dict->SetString(kSetupStartOAuthUrlKey, cloud.GetOAuthUrl());
+  dict->SetString(kSetupStartServiceUrlKey, cloud.GetServiceUrl());
+  dict->SetString(kSetupStartXmppEndpointKey, cloud.GetXmppEndpoint());
+}
+
 std::unique_ptr<base::DictionaryValue> CreateGcdSection(
     const CloudDelegate& cloud) {
   std::unique_ptr<base::DictionaryValue> gcd(new base::DictionaryValue());
-  gcd->SetString(kInfoIdKey, cloud.GetCloudId());
+  SetGcdProperties(cloud, gcd.get());
   SetStateProperties(cloud.GetConnectionState(), gcd.get());
   return gcd;
 }
@@ -756,7 +777,7 @@
 
   std::string ssid;
   std::string passphrase;
-  std::string ticket;
+  RegistrationData registration_data;
   std::string user;
 
   const base::DictionaryValue* wifi = nullptr;
@@ -785,14 +806,26 @@
                    "Only owner can register device");
       return ReturnError(*error, callback);
     }
-    registration->GetString(kSetupStartTicketIdKey, &ticket);
-    if (ticket.empty()) {
+    registration->GetString(kSetupStartTicketIdKey,
+                            &registration_data.ticket_id);
+    if (registration_data.ticket_id.empty()) {
       ErrorPtr error;
       Error::AddToPrintf(&error, FROM_HERE, errors::kInvalidParams,
                          kInvalidParamValueFormat, kSetupStartTicketIdKey, "");
       return ReturnError(*error, callback);
     }
     registration->GetString(kSetupStartUserKey, &user);
+    registration->GetString(kSetupStartClientIdKey,
+                            &registration_data.client_id);
+    registration->GetString(kSetupStartClientSecretKey,
+                            &registration_data.client_secret);
+    registration->GetString(kSetupStartApiKeyKey, &registration_data.api_key);
+    registration->GetString(kSetupStartOAuthUrlKey,
+                            &registration_data.oauth_url);
+    registration->GetString(kSetupStartServiceUrlKey,
+                            &registration_data.service_url);
+    registration->GetString(kSetupStartXmppEndpointKey,
+                            &registration_data.xmpp_endpoint);
   }
 
   cloud_->UpdateDeviceInfo(name, description, location);
@@ -801,7 +834,8 @@
   if (!ssid.empty() && !wifi_->ConfigureCredentials(ssid, passphrase, &error))
     return ReturnError(*error, callback);
 
-  if (!ticket.empty() && !cloud_->Setup(ticket, user, &error))
+  if (!registration_data.ticket_id.empty() &&
+      !cloud_->Setup(registration_data, &error))
     return ReturnError(*error, callback);
 
   ReplyWithSetupStatus(callback);
@@ -822,8 +856,9 @@
     base::DictionaryValue* gcd = new base::DictionaryValue;
     output.Set(kGcdKey, gcd);
     SetStateProperties(state, gcd);
-    if (state.IsStatusEqual(SetupState::kSuccess))
-      gcd->SetString(kInfoIdKey, cloud_->GetCloudId());
+    if (state.IsStatusEqual(SetupState::kSuccess)) {
+      SetGcdProperties(*cloud_, gcd);
+    }
   }
 
   if (wifi_) {
diff --git a/src/privet/privet_handler_unittest.cc b/src/privet/privet_handler_unittest.cc
index 20f5aa0..3c72242 100644
--- a/src/privet/privet_handler_unittest.cc
+++ b/src/privet/privet_handler_unittest.cc
@@ -15,6 +15,7 @@
 #include <base/values.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <weave/device.h>
 #include <weave/test/unittest_utils.h>
 
 #include "src/privet/constants.h"
@@ -36,15 +37,13 @@
 
 void LoadTestJson(const std::string& test_json,
                   base::DictionaryValue* dictionary) {
-  std::string json = test_json;
-  base::ReplaceChars(json, "'", "\"", &json);
   int error = 0;
   std::string message;
   std::unique_ptr<base::Value> value(
-      base::JSONReader::ReadAndReturnError(json, base::JSON_PARSE_RFC, &error,
-                                           &message)
+      base::JSONReader::ReadAndReturnError(test_json, base::JSON_PARSE_RFC,
+                                           &error, &message)
           .release());
-  EXPECT_TRUE(value.get()) << "\nError: " << message << "\n" << json;
+  EXPECT_TRUE(value.get()) << "\nError: " << message << "\n" << test_json;
   base::DictionaryValue* dictionary_ptr = nullptr;
   if (value->GetAsDictionary(&dictionary_ptr))
     dictionary->MergeDictionary(dictionary_ptr);
@@ -58,7 +57,7 @@
 };
 
 std::ostream& operator<<(std::ostream& stream, const CodeWithReason& error) {
-  return stream << "{" << error.code << ", " << error.reason << "}";
+  return stream << R"({" << error.code << ", " << error.reason << "})";
 }
 
 bool IsEqualError(const CodeWithReason& expected,
@@ -134,12 +133,11 @@
     EXPECT_CALL(cloud_, GetCloudId()).WillRepeatedly(Return(""));
     EXPECT_CALL(cloud_, GetConnectionState())
         .WillRepeatedly(ReturnRef(gcd_disabled_state_));
-    auto set_error = [](const std::string&, const std::string&,
-                        ErrorPtr* error) {
+    auto set_error = [](ErrorPtr* error) {
       Error::AddTo(error, FROM_HERE, "setupUnavailable", "");
     };
-    EXPECT_CALL(cloud_, Setup(_, _, _))
-        .WillRepeatedly(DoAll(Invoke(set_error), Return(false)));
+    EXPECT_CALL(cloud_, Setup(_, _))
+        .WillRepeatedly(DoAll(WithArgs<1>(Invoke(set_error)), Return(false)));
   }
 
   test::MockClock clock_;
@@ -219,37 +217,40 @@
       .WillRepeatedly(Return(std::set<AuthType>{}));
 
   const char kExpected[] = R"({
-    'version': '3.0',
-    'id': 'TestId',
-    'name': 'TestDevice',
-    'services': [ "developmentBoard" ],
-    'modelManifestId': "ABMID",
-    'basicModelManifest': {
-      'uiDeviceKind': 'developmentBoard',
-      'oemName': 'Chromium',
-      'modelName': 'Brillo'
+    "version": "3.0",
+    "id": "TestId",
+    "name": "TestDevice",
+    "services": [ "developmentBoard" ],
+    "modelManifestId": "ABMID",
+    "basicModelManifest": {
+      "uiDeviceKind": "developmentBoard",
+      "oemName": "Chromium",
+      "modelName": "Brillo"
     },
-    'endpoints': {
-      'httpPort': 0,
-      'httpUpdatesPort': 0,
-      'httpsPort': 0,
-      'httpsUpdatesPort': 0
+    "endpoints": {
+      "httpPort": 0,
+      "httpUpdatesPort": 0,
+      "httpsPort": 0,
+      "httpsUpdatesPort": 0
     },
-    'authentication': {
-      'anonymousMaxScope': 'user',
-      'mode': [
+    "authentication": {
+      "anonymousMaxScope": "user",
+      "mode": [
       ],
-      'pairing': [
+      "pairing": [
       ],
-      'crypto': [
+      "crypto": [
       ]
     },
-    'gcd': {
-      'id': '',
-      'status': 'disabled'
+    "gcd": {
+      "id": "",
+      "oauth_url": "https://oauths/",
+      "service_url": "https://service/",
+      "status": "disabled",
+      "xmpp_endpoint": "xmpp:678"
     },
-    'time': 1410000001000.0,
-    'sessionId': 'SessionId'
+    "time": 1410000001000.0,
+    "sessionId": "SessionId"
   })";
   EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/info", "{}"));
 }
@@ -266,85 +267,89 @@
       .WillRepeatedly(Return("Test_device.BBABCLAprv"));
 
   const char kExpected[] = R"({
-    'version': '3.0',
-    'id': 'TestId',
-    'name': 'TestDevice',
-    'description': 'TestDescription',
-    'location': 'TestLocation',
-    'services': [ "developmentBoard" ],
-    'modelManifestId': "ABMID",
-    'basicModelManifest': {
-      'uiDeviceKind': 'developmentBoard',
-      'oemName': 'Chromium',
-      'modelName': 'Brillo'
+    "version": "3.0",
+    "id": "TestId",
+    "name": "TestDevice",
+    "description": "TestDescription",
+    "location": "TestLocation",
+    "services": [ "developmentBoard" ],
+    "modelManifestId": "ABMID",
+    "basicModelManifest": {
+      "uiDeviceKind": "developmentBoard",
+      "oemName": "Chromium",
+      "modelName": "Brillo"
     },
-    'endpoints': {
-      'httpPort': 80,
-      'httpUpdatesPort': 10080,
-      'httpsPort': 443,
-      'httpsUpdatesPort': 10443
+    "endpoints": {
+      "httpPort": 80,
+      "httpUpdatesPort": 10080,
+      "httpsPort": 443,
+      "httpsUpdatesPort": 10443
     },
-    'authentication': {
-      'anonymousMaxScope': 'none',
-      'mode': [
-        'anonymous',
-        'pairing',
-        'local'
+    "authentication": {
+      "anonymousMaxScope": "none",
+      "mode": [
+        "anonymous",
+        "pairing",
+        "local"
       ],
-      'pairing': [
-        'pinCode',
-        'embeddedCode'
+      "pairing": [
+        "pinCode",
+        "embeddedCode"
       ],
-      'crypto': [
-        'p224_spake2'
+      "crypto": [
+        "p224_spake2"
       ]
     },
-    'wifi': {
-      'capabilities': [
-        '2.4GHz'
+    "wifi": {
+      "capabilities": [
+        "2.4GHz"
       ],
-      'ssid': 'TestSsid',
-      'hostedSsid': 'Test_device.BBABCLAprv',
-      'status': 'offline'
+      "ssid": "TestSsid",
+      "hostedSsid": "Test_device.BBABCLAprv",
+      "status": "offline"
     },
-    'gcd': {
-      'id': 'TestCloudId',
-      'status': 'online'
+    "gcd": {
+      "id": "TestCloudId",
+      "oauth_url": "https://oauths/",
+      "service_url": "https://service/",
+      "status": "online",
+      "xmpp_endpoint": "xmpp:678"
     },
-    'time': 1410000001000.0,
-    'sessionId': 'SessionId'
+    "time": 1410000001000.0,
+    "sessionId": "SessionId"
   })";
   EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/info", "{}"));
 }
 
 TEST_F(PrivetHandlerTest, PairingStartInvalidParams) {
-  EXPECT_PRED2(IsEqualError, CodeWithReason(400, "invalidParams"),
-               HandleRequest("/privet/v3/pairing/start",
-                             "{'pairing':'embeddedCode','crypto':'crypto'}"));
+  EXPECT_PRED2(
+      IsEqualError, CodeWithReason(400, "invalidParams"),
+      HandleRequest("/privet/v3/pairing/start",
+                    R"({"pairing":"embeddedCode","crypto":"crypto"})"));
 
   EXPECT_PRED2(IsEqualError, CodeWithReason(400, "invalidParams"),
                HandleRequest("/privet/v3/pairing/start",
-                             "{'pairing':'code','crypto':'p224_spake2'}"));
+                             R"({"pairing":"code","crypto":"p224_spake2"})"));
 }
 
 TEST_F(PrivetHandlerTest, PairingStart) {
   EXPECT_JSON_EQ(
-      "{'deviceCommitment': 'testCommitment', 'sessionId': 'testSession'}",
+      R"({"deviceCommitment": "testCommitment", "sessionId": "testSession"})",
       HandleRequest("/privet/v3/pairing/start",
-                    "{'pairing': 'embeddedCode', 'crypto': 'p224_spake2'}"));
+                    R"({"pairing": "embeddedCode", "crypto": "p224_spake2"})"));
 }
 
 TEST_F(PrivetHandlerTest, PairingConfirm) {
   EXPECT_JSON_EQ(
-      "{'certFingerprint':'testFingerprint','certSignature':'testSignature'}",
+      R"({"certFingerprint":"testFingerprint","certSignature":"testSignature"})",
       HandleRequest(
           "/privet/v3/pairing/confirm",
-          "{'sessionId':'testSession','clientCommitment':'testCommitment'}"));
+          R"({"sessionId":"testSession","clientCommitment":"testCommitment"})"));
 }
 
 TEST_F(PrivetHandlerTest, PairingCancel) {
   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/pairing/cancel",
-                                     "{'sessionId': 'testSession'}"));
+                                     R"({"sessionId": "testSession"})"));
 }
 
 TEST_F(PrivetHandlerTest, AuthErrorNoType) {
@@ -354,25 +359,26 @@
 
 TEST_F(PrivetHandlerTest, AuthErrorInvalidType) {
   EXPECT_PRED2(IsEqualError, CodeWithReason(400, "invalidAuthMode"),
-               HandleRequest("/privet/v3/auth", "{'mode':'unknown'}"));
+               HandleRequest("/privet/v3/auth", R"({"mode":"unknown"})"));
 }
 
 TEST_F(PrivetHandlerTest, AuthErrorNoScope) {
   EXPECT_PRED2(IsEqualError, CodeWithReason(400, "invalidRequestedScope"),
-               HandleRequest("/privet/v3/auth", "{'mode':'anonymous'}"));
+               HandleRequest("/privet/v3/auth", R"({"mode":"anonymous"})"));
 }
 
 TEST_F(PrivetHandlerTest, AuthErrorInvalidScope) {
   EXPECT_PRED2(
       IsEqualError, CodeWithReason(400, "invalidRequestedScope"),
       HandleRequest("/privet/v3/auth",
-                    "{'mode':'anonymous','requestedScope':'unknown'}"));
+                    R"({"mode":"anonymous","requestedScope":"unknown"})"));
 }
 
 TEST_F(PrivetHandlerTest, AuthErrorAccessDenied) {
-  EXPECT_PRED2(IsEqualError, CodeWithReason(403, "accessDenied"),
-               HandleRequest("/privet/v3/auth",
-                             "{'mode':'anonymous','requestedScope':'owner'}"));
+  EXPECT_PRED2(
+      IsEqualError, CodeWithReason(403, "accessDenied"),
+      HandleRequest("/privet/v3/auth",
+                    R"({"mode":"anonymous","requestedScope":"owner"})"));
 }
 
 TEST_F(PrivetHandlerTest, AuthErrorInvalidAuthCode) {
@@ -382,9 +388,9 @@
   EXPECT_CALL(security_, CreateAccessToken(_, "testToken", _, _, _, _, _))
       .WillRepeatedly(WithArgs<6>(Invoke(set_error)));
   const char kInput[] = R"({
-    'mode': 'pairing',
-    'requestedScope': 'user',
-    'authCode': 'testToken'
+    "mode": "pairing",
+    "requestedScope": "user",
+    "authCode": "testToken"
   })";
   EXPECT_PRED2(IsEqualError, CodeWithReason(403, "invalidAuthCode"),
                HandleRequest("/privet/v3/auth", kInput));
@@ -392,14 +398,15 @@
 
 TEST_F(PrivetHandlerTest, AuthAnonymous) {
   const char kExpected[] = R"({
-    'accessToken': 'GuestAccessToken',
-    'expiresIn': 15,
-    'scope': 'viewer',
-    'tokenType': 'Privet'
+    "accessToken": "GuestAccessToken",
+    "expiresIn": 15,
+    "scope": "viewer",
+    "tokenType": "Privet"
   })";
-  EXPECT_JSON_EQ(kExpected,
-                 HandleRequest("/privet/v3/auth",
-                               "{'mode':'anonymous','requestedScope':'auto'}"));
+  EXPECT_JSON_EQ(
+      kExpected,
+      HandleRequest("/privet/v3/auth",
+                    R"({"mode":"anonymous","requestedScope":"auto"})"));
 }
 
 TEST_F(PrivetHandlerTest, AuthPairing) {
@@ -409,15 +416,15 @@
                             SetArgPointee<5>(base::TimeDelta::FromSeconds(15)),
                             Return(true)));
   const char kInput[] = R"({
-    'mode': 'pairing',
-    'requestedScope': 'owner',
-    'authCode': 'testToken'
+    "mode": "pairing",
+    "requestedScope": "owner",
+    "authCode": "testToken"
   })";
   const char kExpected[] = R"({
-    'accessToken': 'OwnerAccessToken',
-    'expiresIn': 15,
-    'scope': 'owner',
-    'tokenType': 'Privet'
+    "accessToken": "OwnerAccessToken",
+    "expiresIn": 15,
+    "scope": "owner",
+    "tokenType": "Privet"
   })";
   EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/auth", kInput));
 }
@@ -429,15 +436,15 @@
                             SetArgPointee<5>(base::TimeDelta::FromSeconds(15)),
                             Return(true)));
   const char kInput[] = R"({
-    'mode': 'local',
-    'requestedScope': 'auto',
-    'authCode': 'localAuthToken'
+    "mode": "local",
+    "requestedScope": "auto",
+    "authCode": "localAuthToken"
   })";
   const char kExpected[] = R"({
-    'accessToken': 'UserAccessToken',
-    'expiresIn': 15,
-    'scope': 'user',
-    'tokenType': 'Privet'
+    "accessToken": "UserAccessToken",
+    "expiresIn": 15,
+    "scope": "user",
+    "tokenType": "Privet"
   })";
   EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/auth", kInput));
 }
@@ -449,15 +456,15 @@
                             SetArgPointee<5>(base::TimeDelta::FromSeconds(15)),
                             Return(true)));
   const char kInput[] = R"({
-    'mode': 'local',
-    'requestedScope': 'manager',
-    'authCode': 'localAuthToken'
+    "mode": "local",
+    "requestedScope": "manager",
+    "authCode": "localAuthToken"
   })";
   const char kExpected[] = R"({
-    'accessToken': 'ManagerAccessToken',
-    'expiresIn': 15,
-    'scope': 'manager',
-    'tokenType': 'Privet'
+    "accessToken": "ManagerAccessToken",
+    "expiresIn": 15,
+    "scope": "manager",
+    "tokenType": "Privet"
   })";
   EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/auth", kInput));
 }
@@ -469,9 +476,9 @@
                             SetArgPointee<5>(base::TimeDelta::FromSeconds(1)),
                             Return(true)));
   const char kInput[] = R"({
-    'mode': 'local',
-    'requestedScope': 'manager',
-    'authCode': 'localAuthToken'
+    "mode": "local",
+    "requestedScope": "manager",
+    "authCode": "localAuthToken"
   })";
   EXPECT_PRED2(IsEqualError, CodeWithReason(403, "accessDenied"),
                HandleRequest("/privet/v3/auth", kInput));
@@ -500,9 +507,9 @@
   wifi_.setup_state_ = SetupState{SetupState::kSuccess};
 
   const char kExpected[] = R"({
-    'wifi': {
-        'ssid': 'TestSsid',
-        'status': 'success'
+    "wifi": {
+        "ssid": "TestSsid",
+        "status": "success"
      }
   })";
   EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/setup/status", "{}"));
@@ -514,10 +521,10 @@
   wifi_.setup_state_ = SetupState{std::move(error)};
 
   const char kExpected[] = R"({
-    'wifi': {
-        'status': 'error',
-        'error': {
-          'code': 'invalidPassphrase'
+    "wifi": {
+        "status": "error",
+        "error": {
+          "code": "invalidPassphrase"
         }
      }
   })";
@@ -530,9 +537,12 @@
   cloud_.setup_state_ = SetupState{SetupState::kSuccess};
 
   const char kExpected[] = R"({
-    'gcd': {
-        'id': 'TestCloudId',
-        'status': 'success'
+    "gcd": {
+        "id": "TestCloudId",
+        "oauth_url": "https://oauths/",
+        "service_url": "https://service/",
+        "status": "success",
+        "xmpp_endpoint": "xmpp:678"
      }
   })";
   EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/setup/status", "{}"));
@@ -544,10 +554,10 @@
   cloud_.setup_state_ = SetupState{std::move(error)};
 
   const char kExpected[] = R"({
-    'gcd': {
-        'status': 'error',
-        'error': {
-          'code': 'invalidTicket'
+    "gcd": {
+        "status": "error",
+        "error": {
+          "code": "invalidTicket"
         }
      }
   })";
@@ -561,25 +571,25 @@
               UpdateDeviceInfo("testName", "testDescription", "testLocation"))
       .Times(1);
   const char kInput[] = R"({
-    'name': 'testName',
-    'description': 'testDescription',
-    'location': 'testLocation'
+    "name": "testName",
+    "description": "testDescription",
+    "location": "testLocation"
   })";
   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/setup/start", kInput));
 }
 
 TEST_F(PrivetHandlerSetupTest, InvalidParams) {
   const char kInputWifi[] = R"({
-    'wifi': {
-      'ssid': ''
+    "wifi": {
+      "ssid": ""
     }
   })";
   EXPECT_PRED2(IsEqualError, CodeWithReason(400, "invalidParams"),
                HandleRequest("/privet/v3/setup/start", kInputWifi));
 
   const char kInputRegistration[] = R"({
-    'gcd': {
-      'ticketId': ''
+    "gcd": {
+      "ticketId": ""
     }
   })";
   EXPECT_PRED2(IsEqualError, CodeWithReason(400, "invalidParams"),
@@ -589,14 +599,14 @@
 TEST_F(PrivetHandlerSetupTest, WifiSetupUnavailable) {
   SetNoWifiAndGcd();
   EXPECT_PRED2(IsEqualError, CodeWithReason(400, "setupUnavailable"),
-               HandleRequest("/privet/v3/setup/start", "{'wifi': {}}"));
+               HandleRequest("/privet/v3/setup/start", R"({"wifi": {}})"));
 }
 
 TEST_F(PrivetHandlerSetupTest, WifiSetup) {
   const char kInput[] = R"({
-    'wifi': {
-      'ssid': 'testSsid',
-      'passphrase': 'testPass'
+    "wifi": {
+      "ssid": "testSsid",
+      "passphrase": "testPass"
     }
   })";
   auto set_error = [](const std::string&, const std::string&, ErrorPtr* error) {
@@ -607,8 +617,8 @@
                HandleRequest("/privet/v3/setup/start", kInput));
 
   const char kExpected[] = R"({
-    'wifi': {
-      'status': 'inProgress'
+    "wifi": {
+      "status": "inProgress"
     }
   })";
   wifi_.setup_state_ = SetupState{SetupState::kInProgress};
@@ -620,9 +630,9 @@
 TEST_F(PrivetHandlerSetupTest, GcdSetupUnavailable) {
   SetNoWifiAndGcd();
   const char kInput[] = R"({
-    'gcd': {
-      'ticketId': 'testTicket',
-      'user': 'testUser'
+    "gcd": {
+      "ticketId": "testTicket",
+      "user": "testUser"
     }
   })";
 
@@ -632,39 +642,74 @@
 
 TEST_F(PrivetHandlerSetupTest, GcdSetup) {
   const char kInput[] = R"({
-    'gcd': {
-      'ticketId': 'testTicket',
-      'user': 'testUser'
+    "gcd": {
+      "ticketId": "testTicket",
+      "user": "testUser"
     }
   })";
 
-  auto set_error = [](const std::string&, const std::string&, ErrorPtr* error) {
+  auto set_error = [](ErrorPtr* error) {
     return Error::AddTo(error, FROM_HERE, "deviceBusy", "");
   };
-  EXPECT_CALL(cloud_, Setup(_, _, _)).WillOnce(Invoke(set_error));
+  EXPECT_CALL(cloud_, Setup(_, _)).WillOnce(WithArgs<1>(Invoke(set_error)));
   EXPECT_PRED2(IsEqualError, CodeWithReason(503, "deviceBusy"),
                HandleRequest("/privet/v3/setup/start", kInput));
 
   const char kExpected[] = R"({
-    'gcd': {
-      'status': 'inProgress'
+    "gcd": {
+      "status": "inProgress"
     }
   })";
   cloud_.setup_state_ = SetupState{SetupState::kInProgress};
-  EXPECT_CALL(cloud_, Setup("testTicket", "testUser", _))
+  EXPECT_CALL(cloud_, Setup(RegistrationData{"testTicket"}, _))
       .WillOnce(Return(true));
   EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/setup/start", kInput));
 }
 
+TEST_F(PrivetHandlerSetupTest, GcdSetupWithEndpoints) {
+  const char kInput[] = R"({
+    "gcd": {
+      "api_key": "test_api_key",
+      "client_id": "test_client_id",
+      "client_secret": "test_client_secret",
+      "oauth_url": "https://oauths/",
+      "service_url": "https://service/",
+      "status": "success",
+      "xmpp_endpoint": "xmpp:678",
+      "ticketId": "testTicket",
+      "user": "testUser"
+    }
+  })";
+
+  const char kExpected[] = R"({
+    "gcd": {
+      "status": "inProgress"
+    }
+  })";
+  cloud_.setup_state_ = SetupState{SetupState::kInProgress};
+
+  RegistrationData expected_reg_data;
+  expected_reg_data.ticket_id = "testTicket";
+  expected_reg_data.oauth_url = "https://oauths/";
+  expected_reg_data.client_id = "test_client_id";
+  expected_reg_data.client_secret = "test_client_secret";
+  expected_reg_data.api_key = "test_api_key";
+  expected_reg_data.service_url = "https://service/";
+  expected_reg_data.xmpp_endpoint = "xmpp:678";
+
+  EXPECT_CALL(cloud_, Setup(expected_reg_data, _)).WillOnce(Return(true));
+  EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/setup/start", kInput));
+}
+
 TEST_F(PrivetHandlerSetupTest, GcdSetupAsMaster) {
   EXPECT_CALL(security_, ParseAccessToken(_, _, _))
       .WillRepeatedly(DoAll(
           SetArgPointee<1>(UserInfo{AuthScope::kManager, TestUserId{"1"}}),
           Return(true)));
   const char kInput[] = R"({
-    'gcd': {
-      'ticketId': 'testTicket',
-      'user': 'testUser'
+    "gcd": {
+      "ticketId": "testTicket",
+      "user": "testUser"
     }
   })";
 
@@ -673,59 +718,59 @@
 }
 
 TEST_F(PrivetHandlerTestWithAuth, ClaimAccessControl) {
-  EXPECT_JSON_EQ("{'clientToken': 'RootClientAuthToken'}",
+  EXPECT_JSON_EQ(R"({"clientToken": "RootClientAuthToken"})",
                  HandleRequest("/privet/v3/accessControl/claim", "{}"));
 }
 
 TEST_F(PrivetHandlerTestWithAuth, ConfirmAccessControl) {
   EXPECT_JSON_EQ("{}",
                  HandleRequest("/privet/v3/accessControl/confirm",
-                               "{'clientToken': 'DerivedClientAuthToken'}"));
+                               R"({"clientToken": "DerivedClientAuthToken"})"));
 }
 
 TEST_F(PrivetHandlerTestWithAuth, State) {
-  EXPECT_JSON_EQ("{'state': {'test': {}}, 'fingerprint': '1'}",
+  EXPECT_JSON_EQ(R"({"state": {"test": {}}, "fingerprint": "1"})",
                  HandleRequest("/privet/v3/state", "{}"));
 
   cloud_.NotifyOnStateChanged();
 
-  EXPECT_JSON_EQ("{'state': {'test': {}}, 'fingerprint': '2'}",
+  EXPECT_JSON_EQ(R"({"state": {"test": {}}, "fingerprint": "2"})",
                  HandleRequest("/privet/v3/state", "{}"));
 }
 
 TEST_F(PrivetHandlerTestWithAuth, CommandsDefs) {
-  EXPECT_JSON_EQ("{'commands': {'test':{}}, 'fingerprint': '1'}",
+  EXPECT_JSON_EQ(R"({"commands": {"test":{}}, "fingerprint": "1"})",
                  HandleRequest("/privet/v3/commandDefs", "{}"));
 
   cloud_.NotifyOnTraitDefsChanged();
 
-  EXPECT_JSON_EQ("{'commands': {'test':{}}, 'fingerprint': '2'}",
+  EXPECT_JSON_EQ(R"({"commands": {"test":{}}, "fingerprint": "2"})",
                  HandleRequest("/privet/v3/commandDefs", "{}"));
 }
 
 TEST_F(PrivetHandlerTestWithAuth, Traits) {
-  EXPECT_JSON_EQ("{'traits': {'test': {}}, 'fingerprint': '1'}",
+  EXPECT_JSON_EQ(R"({"traits": {"test": {}}, "fingerprint": "1"})",
                  HandleRequest("/privet/v3/traits", "{}"));
 
   cloud_.NotifyOnTraitDefsChanged();
 
-  EXPECT_JSON_EQ("{'traits': {'test': {}}, 'fingerprint': '2'}",
+  EXPECT_JSON_EQ(R"({"traits": {"test": {}}, "fingerprint": "2"})",
                  HandleRequest("/privet/v3/traits", "{}"));
 }
 
 TEST_F(PrivetHandlerTestWithAuth, Components) {
-  EXPECT_JSON_EQ("{'components': {'test': {}}, 'fingerprint': '1'}",
+  EXPECT_JSON_EQ(R"({"components": {"test": {}}, "fingerprint": "1"})",
                  HandleRequest("/privet/v3/components", "{}"));
 
   cloud_.NotifyOnComponentTreeChanged();
 
-  EXPECT_JSON_EQ("{'components': {'test': {}}, 'fingerprint': '2'}",
+  EXPECT_JSON_EQ(R"({"components": {"test": {}}, "fingerprint": "2"})",
                  HandleRequest("/privet/v3/components", "{}"));
 
   // State change will also change the components fingerprint.
   cloud_.NotifyOnStateChanged();
 
-  EXPECT_JSON_EQ("{'components': {'test': {}}, 'fingerprint': '3'}",
+  EXPECT_JSON_EQ(R"({"components": {"test": {}}, "fingerprint": "3"})",
                  HandleRequest("/privet/v3/components", "{}"));
 }
 
@@ -770,7 +815,7 @@
     "fingerprint": "1"
   })";
   EXPECT_JSON_EQ(kExpected1, HandleRequest("/privet/v3/components",
-                                           "{'filter':['state']}"));
+                                           R"({"filter":["state"]})"));
 
   const char kExpected2[] = R"({
     "components": {
@@ -781,7 +826,7 @@
     "fingerprint": "1"
   })";
   EXPECT_JSON_EQ(kExpected2, HandleRequest("/privet/v3/components",
-                                           "{'filter':['traits']}"));
+                                           R"({"filter":["traits"]})"));
 
   const char kExpected3[] = R"({
     "components": {
@@ -799,7 +844,7 @@
     "fingerprint": "1"
   })";
   EXPECT_JSON_EQ(kExpected3, HandleRequest("/privet/v3/components",
-                                           "{'filter':['components']}"));
+                                           R"({"filter":["components"]})"));
 
   const char kExpected4[] = R"({
     "components": {
@@ -827,9 +872,10 @@
     },
     "fingerprint": "1"
   })";
-  EXPECT_JSON_EQ(kExpected4,
-                 HandleRequest("/privet/v3/components",
-                               "{'filter':['traits', 'components', 'state']}"));
+  EXPECT_JSON_EQ(
+      kExpected4,
+      HandleRequest("/privet/v3/components",
+                    R"({"filter":["traits", "components", "state"]})"));
 
   const base::DictionaryValue* comp2 = nullptr;
   ASSERT_TRUE(components.GetDictionary("comp1.components.comp2", &comp2));
@@ -852,7 +898,7 @@
       kExpected5,
       HandleRequest(
           "/privet/v3/components",
-          "{'path':'comp1.comp2', 'filter':['traits', 'components']}"));
+          R"({"path":"comp1.comp2", "filter":["traits", "components"]})"));
 
   auto error_handler = [](ErrorPtr* error) -> const base::DictionaryValue* {
     return Error::AddTo(error, FROM_HERE, "componentNotFound", "");
@@ -863,36 +909,36 @@
   EXPECT_PRED2(
       IsEqualError, CodeWithReason(500, "componentNotFound"),
       HandleRequest("/privet/v3/components",
-                    "{'path':'comp7', 'filter':['traits', 'components']}"));
+                    R"({"path":"comp7", "filter":["traits", "components"]})"));
 }
 
 TEST_F(PrivetHandlerTestWithAuth, CommandsExecute) {
-  const char kInput[] = "{'name': 'test'}";
+  const char kInput[] = R"({"name": "test"})";
   base::DictionaryValue command;
   LoadTestJson(kInput, &command);
-  LoadTestJson("{'id':'5'}", &command);
+  LoadTestJson(R"({"id":"5"})", &command);
   EXPECT_CALL(cloud_, AddCommand(_, _, _))
       .WillOnce(WithArgs<2>(Invoke(
           [&command](const CloudDelegate::CommandDoneCallback& callback) {
             callback.Run(command, nullptr);
           })));
 
-  EXPECT_JSON_EQ("{'name':'test', 'id':'5'}",
+  EXPECT_JSON_EQ(R"({"name":"test", "id":"5"})",
                  HandleRequest("/privet/v3/commands/execute", kInput));
 }
 
 TEST_F(PrivetHandlerTestWithAuth, CommandsStatus) {
-  const char kInput[] = "{'id': '5'}";
+  const char kInput[] = R"({"id": "5"})";
   base::DictionaryValue command;
   LoadTestJson(kInput, &command);
-  LoadTestJson("{'name':'test'}", &command);
+  LoadTestJson(R"({"name":"test"})", &command);
   EXPECT_CALL(cloud_, GetCommand(_, _, _))
       .WillOnce(WithArgs<2>(Invoke(
           [&command](const CloudDelegate::CommandDoneCallback& callback) {
             callback.Run(command, nullptr);
           })));
 
-  EXPECT_JSON_EQ("{'name':'test', 'id':'5'}",
+  EXPECT_JSON_EQ(R"({"name":"test", "id":"5"})",
                  HandleRequest("/privet/v3/commands/status", kInput));
 
   ErrorPtr error;
@@ -904,11 +950,11 @@
           })));
 
   EXPECT_PRED2(IsEqualError, CodeWithReason(404, "notFound"),
-               HandleRequest("/privet/v3/commands/status", "{'id': '15'}"));
+               HandleRequest("/privet/v3/commands/status", R"({"id": "15"})"));
 }
 
 TEST_F(PrivetHandlerTestWithAuth, CommandsCancel) {
-  const char kExpected[] = "{'id': '5', 'name':'test', 'state':'cancelled'}";
+  const char kExpected[] = R"({"id": "5", "name":"test", "state":"cancelled"})";
   base::DictionaryValue command;
   LoadTestJson(kExpected, &command);
   EXPECT_CALL(cloud_, CancelCommand(_, _, _))
@@ -918,7 +964,7 @@
           })));
 
   EXPECT_JSON_EQ(kExpected,
-                 HandleRequest("/privet/v3/commands/cancel", "{'id': '8'}"));
+                 HandleRequest("/privet/v3/commands/cancel", R"({"id": "8"})"));
 
   ErrorPtr error;
   Error::AddTo(&error, FROM_HERE, "notFound", "");
@@ -929,14 +975,14 @@
           })));
 
   EXPECT_PRED2(IsEqualError, CodeWithReason(404, "notFound"),
-               HandleRequest("/privet/v3/commands/cancel", "{'id': '11'}"));
+               HandleRequest("/privet/v3/commands/cancel", R"({"id": "11"})"));
 }
 
 TEST_F(PrivetHandlerTestWithAuth, CommandsList) {
   const char kExpected[] = R"({
-    'commands' : [
-        {'id':'5', 'state':'cancelled'},
-        {'id':'15', 'state':'inProgress'}
+    "commands" : [
+        {"id":"5", "state":"cancelled"},
+        {"id":"15", "state":"inProgress"}
      ]})";
 
   base::DictionaryValue commands;
@@ -961,10 +1007,10 @@
   cloud_.NotifyOnStateChanged();
   const char kInput[] = "{}";
   const char kExpected[] = R"({
-   'commandsFingerprint': '2',
-   'stateFingerprint': '2',
-   'traitsFingerprint': '2',
-   'componentsFingerprint': '3'
+   "commandsFingerprint": "2",
+   "stateFingerprint": "2",
+   "traitsFingerprint": "2",
+   "componentsFingerprint": "3"
   })";
   EXPECT_JSON_EQ(kExpected,
                  HandleRequest("/privet/v3/checkForUpdates", kInput));
@@ -978,16 +1024,16 @@
   cloud_.NotifyOnComponentTreeChanged();
   cloud_.NotifyOnStateChanged();
   const char kInput[] = R"({
-   'commandsFingerprint': '1',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '1',
-   'componentsFingerprint': '1'
+   "commandsFingerprint": "1",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "1",
+   "componentsFingerprint": "1"
   })";
   const char kExpected[] = R"({
-   'commandsFingerprint': '2',
-   'stateFingerprint': '2',
-   'traitsFingerprint': '2',
-   'componentsFingerprint': '3'
+   "commandsFingerprint": "2",
+   "stateFingerprint": "2",
+   "traitsFingerprint": "2",
+   "componentsFingerprint": "3"
   })";
   EXPECT_JSON_EQ(kExpected,
                  HandleRequest("/privet/v3/checkForUpdates", kInput));
@@ -998,20 +1044,20 @@
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::Max()));
   const char kInput[] = R"({
-   'commandsFingerprint': '1',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '1',
-   'componentsFingerprint': '1'
+   "commandsFingerprint": "1",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "1",
+   "componentsFingerprint": "1"
   })";
   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   EXPECT_EQ(0, GetResponseCount());
   cloud_.NotifyOnTraitDefsChanged();
   EXPECT_EQ(1, GetResponseCount());
   const char kExpected[] = R"({
-   'commandsFingerprint': '2',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '2',
-   'componentsFingerprint': '1'
+   "commandsFingerprint": "2",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "2",
+   "componentsFingerprint": "1"
   })";
   EXPECT_JSON_EQ(kExpected, GetResponse());
 }
@@ -1020,20 +1066,20 @@
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::Max()));
   const char kInput[] = R"({
-   'commandsFingerprint': '1',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '1',
-   'componentsFingerprint': '1'
+   "commandsFingerprint": "1",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "1",
+   "componentsFingerprint": "1"
   })";
   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   EXPECT_EQ(0, GetResponseCount());
   cloud_.NotifyOnTraitDefsChanged();
   EXPECT_EQ(1, GetResponseCount());
   const char kExpected[] = R"({
-   'commandsFingerprint': '2',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '2',
-   'componentsFingerprint': '1'
+   "commandsFingerprint": "2",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "2",
+   "componentsFingerprint": "1"
   })";
   EXPECT_JSON_EQ(kExpected, GetResponse());
 }
@@ -1042,20 +1088,20 @@
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::Max()));
   const char kInput[] = R"({
-   'commandsFingerprint': '1',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '1',
-   'componentsFingerprint': '1'
+   "commandsFingerprint": "1",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "1",
+   "componentsFingerprint": "1"
   })";
   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   EXPECT_EQ(0, GetResponseCount());
   cloud_.NotifyOnStateChanged();
   EXPECT_EQ(1, GetResponseCount());
   const char kExpected[] = R"({
-   'commandsFingerprint': '1',
-   'stateFingerprint': '2',
-   'traitsFingerprint': '1',
-   'componentsFingerprint': '2'
+   "commandsFingerprint": "1",
+   "stateFingerprint": "2",
+   "traitsFingerprint": "1",
+   "componentsFingerprint": "2"
   })";
   EXPECT_JSON_EQ(kExpected, GetResponse());
 }
@@ -1064,20 +1110,20 @@
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::Max()));
   const char kInput[] = R"({
-   'commandsFingerprint': '1',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '1',
-   'componentsFingerprint': '1'
+   "commandsFingerprint": "1",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "1",
+   "componentsFingerprint": "1"
   })";
   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   EXPECT_EQ(0, GetResponseCount());
   cloud_.NotifyOnComponentTreeChanged();
   EXPECT_EQ(1, GetResponseCount());
   const char kExpected[] = R"({
-   'commandsFingerprint': '1',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '1',
-   'componentsFingerprint': '2'
+   "commandsFingerprint": "1",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "1",
+   "componentsFingerprint": "2"
   })";
   EXPECT_JSON_EQ(kExpected, GetResponse());
 }
@@ -1086,8 +1132,8 @@
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::Max()));
   const char kInput[] = R"({
-   'stateFingerprint': '1',
-   'componentsFingerprint': '1'
+   "stateFingerprint": "1",
+   "componentsFingerprint": "1"
   })";
   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   EXPECT_EQ(0, GetResponseCount());
@@ -1096,10 +1142,10 @@
   cloud_.NotifyOnComponentTreeChanged();
   EXPECT_EQ(1, GetResponseCount());
   const char kExpected[] = R"({
-   'commandsFingerprint': '2',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '2',
-   'componentsFingerprint': '2'
+   "commandsFingerprint": "2",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "2",
+   "componentsFingerprint": "2"
   })";
   EXPECT_JSON_EQ(kExpected, GetResponse());
 }
@@ -1108,8 +1154,8 @@
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::Max()));
   const char kInput[] = R"({
-   'commandsFingerprint': '1',
-   'traitsFingerprint': '1'
+   "commandsFingerprint": "1",
+   "traitsFingerprint": "1"
   })";
   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   EXPECT_EQ(0, GetResponseCount());
@@ -1120,10 +1166,10 @@
   cloud_.NotifyOnTraitDefsChanged();
   EXPECT_EQ(1, GetResponseCount());
   const char kExpected[] = R"({
-   'commandsFingerprint': '2',
-   'stateFingerprint': '2',
-   'traitsFingerprint': '2',
-   'componentsFingerprint': '3'
+   "commandsFingerprint": "2",
+   "stateFingerprint": "2",
+   "traitsFingerprint": "2",
+   "componentsFingerprint": "3"
   })";
   EXPECT_JSON_EQ(kExpected, GetResponse());
 }
@@ -1132,17 +1178,17 @@
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::Max()));
   const char kInput[] = R"({
-   'commandsFingerprint': '1',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '1',
-   'componentsFingerprint': '1',
-   'waitTimeout': 0
+   "commandsFingerprint": "1",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "1",
+   "componentsFingerprint": "1",
+   "waitTimeout": 0
   })";
   const char kExpected[] = R"({
-   'commandsFingerprint': '1',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '1',
-   'componentsFingerprint': '1'
+   "commandsFingerprint": "1",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "1",
+   "componentsFingerprint": "1"
   })";
   EXPECT_JSON_EQ(kExpected,
                  HandleRequest("/privet/v3/checkForUpdates", kInput));
@@ -1152,11 +1198,11 @@
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::Max()));
   const char kInput[] = R"({
-   'commandsFingerprint': '1',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '1',
-   'componentsFingerprint': '1',
-   'waitTimeout': 3
+   "commandsFingerprint": "1",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "1",
+   "componentsFingerprint": "1",
+   "waitTimeout": 3
   })";
   base::Closure callback;
   EXPECT_CALL(device_, PostDelayedTask(_, _, base::TimeDelta::FromSeconds(3)))
@@ -1166,10 +1212,10 @@
   callback.Run();
   EXPECT_EQ(1, GetResponseCount());
   const char kExpected[] = R"({
-   'commandsFingerprint': '1',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '1',
-   'componentsFingerprint': '1'
+   "commandsFingerprint": "1",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "1",
+   "componentsFingerprint": "1"
   })";
   EXPECT_JSON_EQ(kExpected, GetResponse());
 }
@@ -1178,10 +1224,10 @@
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::FromMinutes(1)));
   const char kInput[] = R"({
-   'commandsFingerprint': '1',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '1',
-   'componentsFingerprint': '1'
+   "commandsFingerprint": "1",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "1",
+   "componentsFingerprint": "1"
   })";
   base::Closure callback;
   EXPECT_CALL(device_, PostDelayedTask(_, _, base::TimeDelta::FromSeconds(50)))
@@ -1191,10 +1237,10 @@
   callback.Run();
   EXPECT_EQ(1, GetResponseCount());
   const char kExpected[] = R"({
-   'commandsFingerprint': '1',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '1',
-   'componentsFingerprint': '1'
+   "commandsFingerprint": "1",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "1",
+   "componentsFingerprint": "1"
   })";
   EXPECT_JSON_EQ(kExpected, GetResponse());
 }
@@ -1203,10 +1249,10 @@
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::FromSeconds(5)));
   const char kInput[] = R"({
-   'commandsFingerprint': '1',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '1',
-   'componentsFingerprint': '1'
+   "commandsFingerprint": "1",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "1",
+   "componentsFingerprint": "1"
   })";
   EXPECT_JSON_EQ(kInput, HandleRequest("/privet/v3/checkForUpdates", kInput));
   EXPECT_EQ(1, GetResponseCount());
@@ -1216,11 +1262,11 @@
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::FromMinutes(1)));
   const char kInput[] = R"({
-   'commandsFingerprint': '1',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '1',
-   'componentsFingerprint': '1',
-   'waitTimeout': 10
+   "commandsFingerprint": "1",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "1",
+   "componentsFingerprint": "1",
+   "waitTimeout": 10
   })";
   base::Closure callback;
   EXPECT_CALL(device_, PostDelayedTask(_, _, base::TimeDelta::FromSeconds(10)))
@@ -1230,10 +1276,10 @@
   callback.Run();
   EXPECT_EQ(1, GetResponseCount());
   const char kExpected[] = R"({
-   'commandsFingerprint': '1',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '1',
-   'componentsFingerprint': '1'
+   "commandsFingerprint": "1",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "1",
+   "componentsFingerprint": "1"
   })";
   EXPECT_JSON_EQ(kExpected, GetResponse());
 }
@@ -1242,11 +1288,11 @@
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::Max()));
   const char kInput[] = R"({
-   'commandsFingerprint': '1',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '1',
-   'componentsFingerprint': '1',
-   'waitTimeout': 10
+   "commandsFingerprint": "1",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "1",
+   "componentsFingerprint": "1",
+   "waitTimeout": 10
   })";
   base::Closure callback;
   EXPECT_CALL(device_, PostDelayedTask(_, _, base::TimeDelta::FromSeconds(10)))
@@ -1256,10 +1302,10 @@
   cloud_.NotifyOnTraitDefsChanged();
   EXPECT_EQ(1, GetResponseCount());
   const char kExpected[] = R"({
-   'commandsFingerprint': '2',
-   'stateFingerprint': '1',
-   'traitsFingerprint': '2',
-   'componentsFingerprint': '1'
+   "commandsFingerprint": "2",
+   "stateFingerprint": "1",
+   "traitsFingerprint": "2",
+   "componentsFingerprint": "1"
   })";
   EXPECT_JSON_EQ(kExpected, GetResponse());
   callback.Run();
diff --git a/src/weave_unittest.cc b/src/weave_unittest.cc
index b300f57..452ac78 100644
--- a/src/weave_unittest.cc
+++ b/src/weave_unittest.cc
@@ -421,7 +421,8 @@
   InitDnsSdPublishing(true, "DB");
 
   bool done = false;
-  device_->Register("TICKET_ID", base::Bind([this, &done](ErrorPtr error) {
+  device_->Register(RegistrationData{"TICKET_ID"},
+                    base::Bind([this, &done](ErrorPtr error) {
                       EXPECT_FALSE(error);
                       done = true;
                       task_runner_.Break();
@@ -431,7 +432,8 @@
   EXPECT_TRUE(done);
 
   done = false;
-  device_->Register("TICKET_ID2", base::Bind([this, &done](ErrorPtr error) {
+  device_->Register(RegistrationData{"TICKET_ID2"},
+                    base::Bind([this, &done](ErrorPtr error) {
                       EXPECT_TRUE(error->HasError("already_registered"));
                       done = true;
                       task_runner_.Break();