Buffet: Phase 1 of GCD device registration workflow

Implemented the basic device registration workflow in buffet daemon.

Updated buffet_client to perform/test device registration:

Check for device registration:
  buffet_client cr

Getting registered device information:
  buffet_client di

Begin registration (with all default values):
  buffet_client sr

Begin registration with custom parameters:
  buffet_client sr "service_url=http://localhost/buffet&device_kind=coffeePot"

Finalize registration:
  buffet_client fr 4/FsXprlpVsmPw6z7ro7aqU156Eh6V.0ktCYeVc3DwYEnp6UAPFm0GAey3PigI

BUG=chromium:363348
TEST=unit tests passed.

Change-Id: Id8a90b66fbdc366eaa9f62caa82a7cb0abc2e638
Reviewed-on: https://chromium-review.googlesource.com/195082
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Chris Sosa <sosa@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/buffet.conf b/buffet/buffet.conf
index 7c3ee8c..66cd9ed 100644
--- a/buffet/buffet.conf
+++ b/buffet/buffet.conf
@@ -9,4 +9,8 @@
 stop on stopping system-services
 respawn
 
+pre-start script
+  mkdir -p /var/lib/buffet
+end script
+
 exec buffet
diff --git a/buffet/buffet.gyp b/buffet/buffet.gyp
index eb140be..12933d4 100644
--- a/buffet/buffet.gyp
+++ b/buffet/buffet.gyp
@@ -32,6 +32,7 @@
         'dbus_constants.cc',
         'dbus_manager.cc',
         'dbus_utils.cc',
+        'device_registration_info.cc',
         'exported_property_set.cc',
         'http_request.cc',
         'http_transport_curl.cc',
@@ -59,6 +60,9 @@
         'buffet_client.cc',
         'dbus_constants.cc',
       ],
+      'dependencies': [
+        'buffet_common',
+      ],
     },
     {
       'target_name': 'buffet_testrunner',
diff --git a/buffet/buffet_client.cc b/buffet/buffet_client.cc
index edd6e87..dba2f4d 100644
--- a/buffet/buffet_client.cc
+++ b/buffet/buffet_client.cc
@@ -8,17 +8,21 @@
 #include <base/command_line.h>
 #include <base/logging.h>
 #include <base/memory/scoped_ptr.h>
+#include <base/values.h>
 #include <dbus/bus.h>
 #include <dbus/object_proxy.h>
 #include <dbus/message.h>
 #include <sysexits.h>
+#include <dbus/values_util.h>
 
 #include "buffet/dbus_constants.h"
 #include "buffet/dbus_manager.h"
+#include "buffet/data_encoding.h"
 
 using namespace buffet::dbus_constants;
 
 namespace {
+static const int default_timeout_ms = 1000;
 
 dbus::ObjectProxy* GetBuffetDBusProxy(dbus::Bus *bus,
                                       const std::string& object_path) {
@@ -28,11 +32,10 @@
 }
 
 bool CallTestMethod(dbus::ObjectProxy* proxy) {
-  int timeout_ms = 1000;
   dbus::MethodCall method_call(buffet::dbus_constants::kRootInterface,
                                buffet::dbus_constants::kRootTestMethod);
-  scoped_ptr<dbus::Response> response(proxy->CallMethodAndBlock(&method_call,
-                                                                timeout_ms));
+  scoped_ptr<dbus::Response> response(
+    proxy->CallMethodAndBlock(&method_call, default_timeout_ms));
   if (!response) {
     std::cout << "Failed to receive a response." << std::endl;
     return false;
@@ -41,33 +44,119 @@
   return true;
 }
 
-bool CallManagerRegisterDevice(dbus::ObjectProxy* proxy,
-                               const std::string& client_id,
-                               const std::string& client_secret,
-                               const std::string& api_key) {
+bool CallManagerCheckDeviceRegistered(dbus::ObjectProxy* proxy) {
   dbus::MethodCall method_call(
-      buffet::dbus_constants::kManagerInterface,
-      buffet::dbus_constants::kManagerRegisterDeviceMethod);
-  dbus::MessageWriter writer(&method_call);
-  writer.AppendString(client_id);
-  writer.AppendString(client_secret);
-  writer.AppendString(api_key);
-  int timeout_ms = 1000;
+    buffet::dbus_constants::kManagerInterface,
+    buffet::dbus_constants::kManagerCheckDeviceRegistered);
+
   scoped_ptr<dbus::Response> response(
-      proxy->CallMethodAndBlock(&method_call, timeout_ms));
+    proxy->CallMethodAndBlock(&method_call, default_timeout_ms));
   if (!response) {
     std::cout << "Failed to receive a response." << std::endl;
     return false;
   }
 
   dbus::MessageReader reader(response.get());
-  std::string registration_id;
-  if (!reader.PopString(&registration_id)) {
-    std::cout << "No registration id in response." << std::endl;
+  std::string device_id;
+  if (!reader.PopString(&device_id)) {
+    std::cout << "No device ID in response." << std::endl;
     return false;
   }
 
-  std::cout << "Registration ID is " << registration_id << std::endl;
+  std::cout << "Device ID: "
+            << (device_id.empty() ? std::string("<unregistered>") : device_id)
+            << std::endl;
+  return true;
+}
+
+bool CallManagerGetDeviceInfo(dbus::ObjectProxy* proxy) {
+  dbus::MethodCall method_call(
+    buffet::dbus_constants::kManagerInterface,
+    buffet::dbus_constants::kManagerGetDeviceInfo);
+
+  scoped_ptr<dbus::Response> response(
+    proxy->CallMethodAndBlock(&method_call, default_timeout_ms));
+  if (!response) {
+    std::cout << "Failed to receive a response." << std::endl;
+    return false;
+  }
+
+  dbus::MessageReader reader(response.get());
+  std::string device_info;
+  if (!reader.PopString(&device_info)) {
+    std::cout << "No device info in response." << std::endl;
+    return false;
+  }
+
+  std::cout << "Device Info: "
+    << (device_info.empty() ? std::string("<unregistered>") : device_info)
+    << std::endl;
+  return true;
+}
+
+bool CallManagerStartRegisterDevice(
+    dbus::ObjectProxy* proxy,
+    const std::map<std::string, std::shared_ptr<base::Value>>& params) {
+  dbus::MethodCall method_call(
+      buffet::dbus_constants::kManagerInterface,
+      buffet::dbus_constants::kManagerStartRegisterDevice);
+  dbus::MessageWriter writer(&method_call);
+  dbus::MessageWriter dict_writer(nullptr);
+  writer.OpenArray("{sv}", &dict_writer);
+  for (auto&& pair : params) {
+    dbus::MessageWriter dict_entry_writer(nullptr);
+    dict_writer.OpenDictEntry(&dict_entry_writer);
+    dict_entry_writer.AppendString(pair.first);
+    dbus::AppendBasicTypeValueDataAsVariant(&dict_entry_writer,
+                                            *pair.second.get());
+    dict_writer.CloseContainer(&dict_entry_writer);
+  }
+  writer.CloseContainer(&dict_writer);
+
+  static const int timeout_ms = 3000;
+  scoped_ptr<dbus::Response> response(
+    proxy->CallMethodAndBlock(&method_call, timeout_ms));
+  if (!response) {
+    std::cout << "Failed to receive a response." << std::endl;
+    return false;
+  }
+
+  dbus::MessageReader reader(response.get());
+  std::string info;
+  if (!reader.PopString(&info)) {
+    std::cout << "No valid response." << std::endl;
+    return false;
+  }
+
+  std::cout << "Registration started: " << info << std::endl;
+  return true;
+}
+
+bool CallManagerFinishRegisterDevice(dbus::ObjectProxy* proxy,
+                                     const std::string& user_auth_code) {
+  dbus::MethodCall method_call(
+    buffet::dbus_constants::kManagerInterface,
+    buffet::dbus_constants::kManagerFinishRegisterDevice);
+  dbus::MessageWriter writer(&method_call);
+  writer.AppendString(user_auth_code);
+  static const int timeout_ms = 10000;
+  scoped_ptr<dbus::Response> response(
+    proxy->CallMethodAndBlock(&method_call, timeout_ms));
+  if (!response) {
+    std::cout << "Failed to receive a response." << std::endl;
+    return false;
+  }
+
+  dbus::MessageReader reader(response.get());
+  std::string device_id;
+  if (!reader.PopString(&device_id)) {
+    std::cout << "No device ID in response." << std::endl;
+    return false;
+  }
+
+  std::cout << "Device ID is "
+            << (device_id.empty() ? std::string("<unregistered>") : device_id)
+            << std::endl;
   return true;
 }
 
@@ -78,9 +167,8 @@
       buffet::dbus_constants::kManagerUpdateStateMethod);
   dbus::MessageWriter writer(&method_call);
   writer.AppendString(json_blob);
-  int timeout_ms = 1000;
   scoped_ptr<dbus::Response> response(
-      proxy->CallMethodAndBlock(&method_call, timeout_ms));
+    proxy->CallMethodAndBlock(&method_call, default_timeout_ms));
   if (!response) {
     std::cout << "Failed to receive a response." << std::endl;
     return false;
@@ -91,8 +179,12 @@
 void usage() {
   std::cerr << "Possible commands:" << std::endl;
   std::cerr << "  " << kRootTestMethod << std::endl;
-  std::cerr << "  " << kManagerRegisterDeviceMethod
-            << "  " << " <client id> <client secret> <api key>" << std::endl;
+  std::cerr << "  " << kManagerCheckDeviceRegistered << std::endl;
+  std::cerr << "  " << kManagerGetDeviceInfo << std::endl;
+  std::cerr << "  " << kManagerStartRegisterDevice
+                    << " param1 = val1&param2 = val2..." << std::endl;
+  std::cerr << "  " << kManagerFinishRegisterDevice
+                    << " device_id" << std::endl;
   std::cerr << "  " << kManagerUpdateStateMethod << std::endl;
 }
 
@@ -107,39 +199,86 @@
   scoped_refptr<dbus::Bus> bus(new dbus::Bus(options));
 
   CommandLine::StringVector args = cl->GetArgs();
-  if (args.size() < 1) {
+  if (args.empty()) {
     usage();
     return EX_USAGE;
   }
 
   // Pop the command off of the args list.
-  std::string command = args[0];
+  std::string command = args.front();
   args.erase(args.begin());
   bool success = false;
   if (command.compare(kRootTestMethod) == 0) {
     auto proxy = GetBuffetDBusProxy(
         bus, buffet::dbus_constants::kRootServicePath);
     success = CallTestMethod(proxy);
-  } else if (command.compare(kManagerRegisterDeviceMethod) == 0) {
-    if (args.size() != 3) {
+  } else if (command.compare(kManagerCheckDeviceRegistered) == 0 ||
+             command.compare("cr") == 0) {
+    if (!args.empty()) {
       std::cerr << "Invalid number of arguments for "
-                << "Manager.RegisterDevice" << std::endl;
+        << "Manager." << kManagerCheckDeviceRegistered << std::endl;
+      usage();
+      return EX_USAGE;
+    }
+    auto proxy = GetBuffetDBusProxy(
+      bus, buffet::dbus_constants::kManagerServicePath);
+    success = CallManagerCheckDeviceRegistered(proxy);
+  } else if (command.compare(kManagerGetDeviceInfo) == 0 ||
+             command.compare("di") == 0) {
+    if (!args.empty()) {
+      std::cerr << "Invalid number of arguments for "
+        << "Manager." << kManagerGetDeviceInfo << std::endl;
+      usage();
+      return EX_USAGE;
+    }
+    auto proxy = GetBuffetDBusProxy(
+      bus, buffet::dbus_constants::kManagerServicePath);
+    success = CallManagerGetDeviceInfo(proxy);
+  } else if (command.compare(kManagerStartRegisterDevice) == 0 ||
+             command.compare("sr") == 0) {
+    if (args.size() > 1) {
+      std::cerr << "Invalid number of arguments for "
+                << "Manager." << kManagerStartRegisterDevice << std::endl;
       usage();
       return EX_USAGE;
     }
     auto proxy = GetBuffetDBusProxy(
         bus, buffet::dbus_constants::kManagerServicePath);
-    success = CallManagerRegisterDevice(proxy, args[0], args[1], args[2]);
-  } else if (command.compare(kManagerUpdateStateMethod) == 0) {
+    std::map<std::string, std::shared_ptr<base::Value>> params;
+
+    if (!args.empty()) {
+      auto key_values = chromeos::data_encoding::WebParamsDecode(args.front());
+      for (auto&& pair : key_values) {
+        params.insert(std::make_pair(
+          pair.first, std::shared_ptr<base::Value>(
+              base::Value::CreateStringValue(pair.second))));
+      }
+    }
+
+    success = CallManagerStartRegisterDevice(proxy, params);
+  } else if (command.compare(kManagerFinishRegisterDevice) == 0 ||
+             command.compare("fr") == 0) {
+    if (args.size() > 1) {
+      std::cerr << "Invalid number of arguments for "
+                << "Manager." << kManagerFinishRegisterDevice << std::endl;
+      usage();
+      return EX_USAGE;
+    }
+    auto proxy = GetBuffetDBusProxy(
+        bus, buffet::dbus_constants::kManagerServicePath);
+    success = CallManagerFinishRegisterDevice(
+        proxy, !args.empty() ? args.front() : std::string());
+  } else if (command.compare(kManagerUpdateStateMethod) == 0 ||
+             command.compare("us") == 0) {
     if (args.size() != 1) {
       std::cerr << "Invalid number of arguments for "
-                << "Manager.UpdateState" << std::endl;
+                << "Manager." << kManagerUpdateStateMethod << std::endl;
       usage();
       return EX_USAGE;
     }
     auto proxy = GetBuffetDBusProxy(
         bus, buffet::dbus_constants::kManagerServicePath);
-    success = CallManagerUpdateState(proxy, args[0]);
+    success = CallManagerUpdateState(proxy, args.front());
   } else {
     std::cerr << "Unknown command: " << command << std::endl;
     usage();
diff --git a/buffet/dbus_constants.cc b/buffet/dbus_constants.cc
index 746058e..f352bbe 100644
--- a/buffet/dbus_constants.cc
+++ b/buffet/dbus_constants.cc
@@ -18,8 +18,11 @@
 const char kManagerInterface[] = "org.chromium.Buffet.Manager";
 const char kManagerServicePath[] = "/org/chromium/Buffet/Manager";
 
-const char kManagerUpdateStateMethod[] = "UpdateState";
-const char kManagerRegisterDeviceMethod[] = "RegisterDevice";
+const char kManagerCheckDeviceRegistered[]  = "CheckDeviceRegistered";
+const char kManagerGetDeviceInfo[]          = "GetDeviceInfo";
+const char kManagerStartRegisterDevice[]    = "StartRegisterDevice";
+const char kManagerFinishRegisterDevice[]   = "FinishRegisterDevice";
+const char kManagerUpdateStateMethod[]      = "UpdateState";
 
 }  // namespace dbus_constants
 
diff --git a/buffet/dbus_constants.h b/buffet/dbus_constants.h
index e30fdde..13c8a71 100644
--- a/buffet/dbus_constants.h
+++ b/buffet/dbus_constants.h
@@ -24,8 +24,11 @@
 extern const char kManagerServicePath[];
 
 // Methods exposed as part of kManagerInterface.
+extern const char kManagerCheckDeviceRegistered[];
+extern const char kManagerGetDeviceInfo[];
+extern const char kManagerStartRegisterDevice[];
+extern const char kManagerFinishRegisterDevice[];
 extern const char kManagerUpdateStateMethod[];
-extern const char kManagerRegisterDeviceMethod[];
 
 }  // namespace dbus_constants
 
diff --git a/buffet/device_registration_info.cc b/buffet/device_registration_info.cc
new file mode 100644
index 0000000..c2b7780
--- /dev/null
+++ b/buffet/device_registration_info.cc
@@ -0,0 +1,471 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "buffet/device_registration_info.h"
+
+#include <base/json/json_reader.h>
+#include <base/json/json_writer.h>
+#include <base/values.h>
+#include <base/file_util.h>
+#include <memory>
+
+#include "buffet/http_utils.h"
+#include "buffet/mime_utils.h"
+#include "buffet/string_utils.h"
+#include "buffet/data_encoding.h"
+
+using namespace chromeos::http;
+using namespace chromeos::data_encoding;
+
+namespace {
+// Persistent keys
+const char kClientId[]      = "client_id";
+const char kClientSecret[]  = "client_secret";
+const char kApiKey[]        = "api_key";
+const char kRefreshToken[]  = "refresh_token";
+const char kDeviceId[]      = "device_id";
+const char kOAuthURL[]      = "oauth_url";
+const char kServiceURL[]    = "service_url";
+const char kRobotAccount[]  = "robot_account";
+// Transient keys
+const char kDeviceKind[]    = "device_kind";
+const char kSystemName[]    = "system_name";
+const char kDisplayName[]   = "display_name";
+
+const base::FilePath::CharType kDeviceInfoFilePath[] =
+    FILE_PATH_LITERAL("/var/lib/buffet/device_reg_info");
+
+bool GetParamValue(
+  const std::map<std::string, std::shared_ptr<base::Value>>& params,
+  const std::string& param_name,
+  std::string* param_value) {
+  auto p = params.find(param_name);
+  if (p == params.end())
+    return false;
+
+  return p->second->GetAsString(param_value);
+}
+
+std::pair<std::string, std::string> BuildAuthHeader(
+    const std::string& access_token_type,
+    const std::string& access_token) {
+  std::string authorization = chromeos::string_utils::Join(' ',
+                                                           access_token_type,
+                                                           access_token);
+  return {request_header::kAuthorization, authorization};
+}
+
+std::unique_ptr<base::DictionaryValue> ParseOAuthResponse(
+  const Response* response, std::string* error_message) {
+  int code = 0;
+  auto resp = ParseJsonResponse(response, &code, error_message);
+  if (resp && code >= status_code::BadRequest) {
+    if (error_message) {
+      error_message->clear();
+      std::string error_code, error;
+      if (resp->GetString("error", &error_code) &&
+          resp->GetString("error_description", &error)) {
+        *error_message = error_code + " (" + error + ")";
+      } else {
+        *error_message = "Unexpected OAuth error";
+      }
+    }
+    return std::unique_ptr<base::DictionaryValue>();
+  }
+  return resp;
+}
+
+std::string BuildURL(std::string url,
+                     const std::string& subpath,
+                     const WebParamList& params) {
+  if (!subpath.empty()) {
+    if (!url.empty() && url.back() != '/')
+      url += '/';
+    url += subpath;
+  }
+
+  if (!params.empty()) {
+    url += '?';
+    url += WebParamsEncode(params);
+  }
+  return url;
+}
+
+
+} // anonymous namespace
+
+namespace buffet {
+
+std::pair<std::string, std::string>
+    DeviceRegistrationInfo::GetAuthorizationHeader() const {
+  return BuildAuthHeader(/*"Bearer"*/"OAuth", access_token_);
+}
+
+std::string DeviceRegistrationInfo::GetServiceURL(
+    const std::string& subpath, const WebParamList& params) const {
+  return BuildURL(service_url_, subpath, params);
+}
+
+std::string DeviceRegistrationInfo::GetDeviceURL(
+    const std::string& subpath, const WebParamList& params) const {
+  CHECK(!device_id_.empty()) << "Must have a valid device ID";
+  std::string path = "devices/" + device_id_;
+  if (!subpath.empty()) {
+    path += '/' + subpath;
+  }
+  return GetServiceURL(path, params);
+}
+
+std::string DeviceRegistrationInfo::GetOAuthURL(const std::string& subpath,
+                                    const WebParamList& params) const {
+  return BuildURL(oauth_url_, subpath, params);
+}
+
+std::string DeviceRegistrationInfo::GetDeviceId() {
+  return CheckRegistration() ? device_id_ : std::string();
+}
+
+bool DeviceRegistrationInfo::Load() {
+  // TODO(avakulenko): Figure out security implications of storing
+  // this data unencrypted.
+  std::string json;
+  if (!base::ReadFileToString(base::FilePath(kDeviceInfoFilePath), &json))
+    return false;
+
+  auto value = std::unique_ptr<base::Value>(base::JSONReader::Read(json));
+  const base::DictionaryValue* dict = nullptr;
+  if (!value || !value->GetAsDictionary(&dict))
+    return false;
+
+  // Get the values into temp variables first to make sure we can get
+  // all the data correctly before changing the state of this object.
+  std::string client_id;
+  if (!dict->GetString(kClientId, &client_id))
+    return false;
+  std::string client_secret;
+  if (!dict->GetString(kClientSecret, &client_secret))
+    return false;
+  std::string api_key;
+  if (!dict->GetString(kApiKey, &api_key))
+    return false;
+  std::string refresh_token;
+  if (!dict->GetString(kRefreshToken, &refresh_token))
+    return false;
+  std::string device_id;
+  if (!dict->GetString(kDeviceId, &device_id))
+    return false;
+  std::string oauth_url;
+  if (!dict->GetString(kOAuthURL, &oauth_url))
+    return false;
+  std::string service_url;
+  if (!dict->GetString(kServiceURL, &service_url))
+    return false;
+  std::string device_robot_account;
+  if (!dict->GetString(kRobotAccount, &device_robot_account))
+    return false;
+
+  client_id_            = client_id;
+  client_secret_        = client_secret;
+  api_key_              = api_key;
+  refresh_token_        = refresh_token;
+  device_id_            = device_id;
+  oauth_url_            = oauth_url;
+  service_url_          = service_url;
+  device_robot_account_ = device_robot_account;
+  return true;
+}
+
+bool DeviceRegistrationInfo::Save() const {
+  // TODO(avakulenko): Figure out security implications of storing
+  // this data unencrypted.
+  base::DictionaryValue dict;
+  dict.SetString(kClientId,     client_id_);
+  dict.SetString(kClientSecret, client_secret_);
+  dict.SetString(kApiKey,       api_key_);
+  dict.SetString(kRefreshToken, refresh_token_);
+  dict.SetString(kDeviceId,     device_id_);
+  dict.SetString(kOAuthURL,     oauth_url_);
+  dict.SetString(kServiceURL,   service_url_);
+  dict.SetString(kRobotAccount, device_robot_account_);
+
+  std::string json;
+  base::JSONWriter::WriteWithOptions(&dict,
+                                     base::JSONWriter::OPTIONS_PRETTY_PRINT,
+                                     &json);
+  int count = file_util::WriteFile(base::FilePath(kDeviceInfoFilePath),
+                                   json.data(), static_cast<int>(json.size()));
+
+  return (count > 0);
+}
+
+bool DeviceRegistrationInfo::CheckRegistration() {
+  LOG(INFO) << "Checking device registration record.";
+  if (refresh_token_.empty() ||
+      device_id_.empty() ||
+      device_robot_account_.empty()) {
+    LOG(INFO) << "No valid device registration record found.";
+    return false;
+  }
+
+  LOG(INFO) << "Device registration record found.";
+  return ValidateAndRefreshAccessToken();
+}
+
+bool DeviceRegistrationInfo::ValidateAndRefreshAccessToken() {
+  LOG(INFO) << " Checking access token expiration.";
+  if (!access_token_.empty() &&
+      !access_token_expiration_.is_null() &&
+      access_token_expiration_ > base::Time::Now()) {
+    LOG(INFO) << "Access token is still valid.";
+    return true;
+  }
+
+  auto response = PostFormData(GetOAuthURL("token"), {
+    {"refresh_token", refresh_token_},
+    {"client_id", client_id_},
+    {"client_secret", client_secret_},
+    {"grant_type", "refresh_token"},
+  });
+  if (!response)
+    return false;
+
+  std::string error;
+  auto json = ParseOAuthResponse(response.get(), &error);
+  if (!json) {
+    LOG(ERROR) << "Unable to refresh access token: " << error;
+    return false;
+  }
+
+  int expires_in = 0;
+  if (!json->GetString("access_token", &access_token_) ||
+      !json->GetInteger("expires_in", &expires_in) ||
+      access_token_.empty() ||
+      expires_in <= 0) {
+    LOG(ERROR) << "Access token unavailable.";
+    return false;
+  }
+
+  access_token_expiration_ = base::Time::Now() +
+                             base::TimeDelta::FromSeconds(expires_in);
+
+  LOG(INFO) << "Access token is refreshed for additional " << expires_in
+            << " seconds.";
+  return true;
+}
+
+std::unique_ptr<base::Value> DeviceRegistrationInfo::GetDeviceInfo() {
+  if (!CheckRegistration())
+    return std::unique_ptr<base::Value>();
+
+  auto response = Get(GetDeviceURL(), {GetAuthorizationHeader()});
+  int status_code = 0;
+  std::unique_ptr<base::Value> device_info =
+      ParseJsonResponse(response.get(), &status_code, nullptr);
+
+  if (device_info) {
+    if (status_code >= status_code::BadRequest) {
+      LOG(WARNING) << "Failed to retrieve the device info. Response code = "
+                   << status_code;
+      return std::unique_ptr<base::Value>();
+    }
+  }
+  return device_info;
+}
+
+bool CheckParam(const std::string& param_name,
+                const std::string& param_value,
+                std::string* error_msg) {
+  if (!param_value.empty())
+    return true;
+
+  if (error_msg)
+    *error_msg = "Parameter " + param_name + " not specified";
+  return false;
+}
+
+std::string DeviceRegistrationInfo::StartRegistration(
+    const std::map<std::string, std::shared_ptr<base::Value>>& params,
+  std::string* error_msg) {
+  GetParamValue(params, kClientId, &client_id_);
+  GetParamValue(params, kClientSecret, &client_secret_);
+  GetParamValue(params, kApiKey, &api_key_);
+  GetParamValue(params, kDeviceId, &device_id_);
+  GetParamValue(params, kDeviceKind, &device_kind_);
+  GetParamValue(params, kSystemName, &system_name_);
+  GetParamValue(params, kDisplayName, &display_name_);
+  GetParamValue(params, kOAuthURL, &oauth_url_);
+  GetParamValue(params, kServiceURL, &service_url_);
+
+  if (!CheckParam(kClientId, client_id_, error_msg))
+    return std::string();
+  if (!CheckParam(kClientSecret, client_secret_, error_msg))
+    return std::string();
+  if (!CheckParam(kApiKey, api_key_, error_msg))
+    return std::string();
+  if (!CheckParam(kDeviceKind, device_kind_, error_msg))
+    return std::string();
+  if (!CheckParam(kSystemName, system_name_, error_msg))
+    return std::string();
+  if (!CheckParam(kOAuthURL, oauth_url_, error_msg))
+    return std::string();
+  if (!CheckParam(kServiceURL, service_url_, error_msg))
+    return std::string();
+
+  std::vector<std::pair<std::string, std::vector<std::string>>> commands = {
+    {"SetDeviceConfiguration", {"data"}}
+  };
+
+  base::DictionaryValue req_json;
+  base::ListValue* set_device_configuration_params = new base::ListValue;
+  base::DictionaryValue* param1 = new base::DictionaryValue;
+  param1->SetString("name", "data");
+  set_device_configuration_params->Append(param1);
+
+  base::ListValue* vendor_commands = new base::ListValue;
+  for (auto&& pair : commands) {
+    base::ListValue* params = new base::ListValue;
+    for (auto&& param_name : pair.second) {
+      base::DictionaryValue* param = new base::DictionaryValue;
+      param->SetString("name", param_name);
+      params->Append(param);
+    }
+    base::DictionaryValue* command = new base::DictionaryValue;
+    command->SetString("name", pair.first);
+    command->Set("parameter", params);
+    vendor_commands->Append(command);
+  }
+
+  req_json.SetString("oauthClientId", client_id_);
+  req_json.SetString("deviceDraft.deviceKind", device_kind_);
+  req_json.SetString("deviceDraft.systemName", system_name_);
+  req_json.SetString("deviceDraft.displayName", display_name_);
+  req_json.SetString("deviceDraft.channel.supportedType", "xmpp");
+  req_json.Set("deviceDraft.commands.base.vendorCommands", vendor_commands);
+
+  std::string url = GetServiceURL("registrationTickets", {{"key", api_key_}});
+  auto resp_json = ParseJsonResponse(PostJson(url, &req_json).get(),
+                                     nullptr, error_msg);
+  if (!resp_json)
+    return std::string();
+
+  const base::DictionaryValue* resp_dict = nullptr;
+  if (!resp_json->GetAsDictionary(&resp_dict)) {
+    if (error_msg)
+      *error_msg = "Invalid response received";
+    return std::string();
+  }
+
+  if (!resp_dict->GetString("id", &ticket_id_))
+    return std::string();
+
+  std::string auth_url = GetOAuthURL("auth", {
+    {"scope", "https://www.googleapis.com/auth/clouddevices"},
+    {"redirect_uri", "urn:ietf:wg:oauth:2.0:oob"},
+    {"response_type", "code"},
+    {"client_id", client_id_}
+  });
+
+  base::DictionaryValue json;
+  json.SetString("ticket_id", ticket_id_);
+  json.SetString("auth_url", auth_url);
+
+  std::string ret;
+  base::JSONWriter::Write(&json, &ret);
+  return ret;
+}
+
+bool DeviceRegistrationInfo::FinishRegistration(
+    const std::string& user_auth_code) {
+  if (ticket_id_.empty()) {
+    LOG(ERROR) << "Finish registration without ticket ID";
+    return false;
+  }
+
+  std::string url = GetServiceURL("registrationTickets/" + ticket_id_);
+  std::unique_ptr<Response> response;
+  if (!user_auth_code.empty()) {
+    std::string user_access_token;
+    response = PostFormData(GetOAuthURL("token"), {
+      {"code", user_auth_code},
+      {"client_id", client_id_},
+      {"client_secret", client_secret_},
+      {"redirect_uri", "urn:ietf:wg:oauth:2.0:oob"},
+      {"grant_type", "authorization_code"}
+    });
+    if (!response)
+      return false;
+
+    auto json_resp = ParseOAuthResponse(response.get(), nullptr);
+    if (!json_resp ||
+        !json_resp->GetString("access_token", &user_access_token)) {
+      return false;
+    }
+
+    base::DictionaryValue user_info;
+    user_info.SetString("userEmail", "me");
+    response = PatchJson(url, &user_info,
+                         {BuildAuthHeader("Bearer", user_access_token)});
+
+    std::string error;
+    auto json = ParseJsonResponse(response.get(), nullptr, &error);
+    if (!json) {
+      LOG(ERROR) << "Error populating user info: " << error;
+      return false;
+    }
+  }
+
+  std::string auth_code;
+  url += "/finalize?key=" + api_key_;
+  do {
+    LOG(INFO) << "Sending request to: " << url;
+    response = PostBinary(url, nullptr, 0);
+    if (response) {
+      if (response->GetStatusCode() == status_code::BadRequest)
+        sleep(1);
+    }
+  }
+  while (response &&
+         response->GetStatusCode() == status_code::BadRequest);
+  if (response &&
+      response->GetStatusCode() == status_code::Ok) {
+    auto json_resp = ParseJsonResponse(response.get(), nullptr, nullptr);
+    if (json_resp &&
+        json_resp->GetString("robotAccountEmail", &device_robot_account_) &&
+        json_resp->GetString("robotAccountAuthorizationCode", &auth_code) &&
+        json_resp->GetString("deviceDraft.id", &device_id_)) {
+      // Now get access_token and refresh_token
+      response = PostFormData(GetOAuthURL("token"), {
+        {"code", auth_code},
+        {"client_id", client_id_},
+        {"client_secret", client_secret_},
+        {"redirect_uri", "oob"},
+        {"scope", "https://www.googleapis.com/auth/clouddevices"},
+        {"grant_type", "authorization_code"}
+      });
+      if (!response)
+        return false;
+
+      json_resp = ParseOAuthResponse(response.get(), nullptr);
+      int expires_in = 0;
+      if (!json_resp ||
+          !json_resp->GetString("access_token", &access_token_) ||
+          !json_resp->GetString("refresh_token", &refresh_token_) ||
+          !json_resp->GetInteger("expires_in", &expires_in) ||
+          access_token_.empty() ||
+          refresh_token_.empty() ||
+          expires_in <= 0) {
+        LOG(ERROR) << "Access token unavailable";
+        return false;
+      }
+
+      access_token_expiration_ = base::Time::Now() +
+                                 base::TimeDelta::FromSeconds(expires_in);
+
+      Save();
+    }
+  }
+  return true;
+}
+
+}  // namespace buffet
diff --git a/buffet/device_registration_info.h b/buffet/device_registration_info.h
new file mode 100644
index 0000000..88a655b
--- /dev/null
+++ b/buffet/device_registration_info.h
@@ -0,0 +1,122 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BUFFET_DEVICE_INFO_H_
+#define BUFFET_DEVICE_INFO_H_
+
+#include <base/basictypes.h>
+#include <base/time/time.h>
+#include <string>
+#include <map>
+#include <memory>
+
+#include "buffet/data_encoding.h"
+
+namespace base {
+  class Value;
+}  // namespace base
+
+namespace buffet {
+
+// The DeviceRegistrationInfo class represents device registration information.
+  class DeviceRegistrationInfo {
+ public:
+   DeviceRegistrationInfo() = default;
+
+  // Returns the authorization HTTP header that can be used to talk
+  // to GCD server for authenticated device communication.
+  // Make sure CheckRegistration() is called before this call.
+  std::pair<std::string, std::string> GetAuthorizationHeader() const;
+
+  // Returns the GCD service request URL. If |subpath| is specified, it is
+  // appended to the base URL which is normally
+  //    https://www.googleapis.com/clouddevices/v1/".
+  // If |params| are specified, each key-value pair is formatted using
+  // chromeos::data_encoding::WebParamsEncode() and appended to URL as a query
+  // string.
+  // So, calling:
+  //    GetServiceURL("ticket", {{"key","apiKey"}})
+  // will return something like:
+  //    https://www.googleapis.com/clouddevices/v1/ticket?key=apiKey
+  std::string GetServiceURL(
+      const std::string& subpath = {},
+      const chromeos::data_encoding::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/clouddevices/v1/devices/<device_id>/
+  std::string GetDeviceURL(
+    const std::string& subpath = {},
+    const chromeos::data_encoding::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 = {},
+    const chromeos::data_encoding::WebParamList& params = {}) const;
+
+  // Returns the registered device ID (GUID) or empty string if failed
+  std::string GetDeviceId();
+
+  // Loads the device registration information from cache.
+  bool Load();
+
+  // Checks for the valid device registration as well as refreshes
+  // the device access token, if available.
+  bool CheckRegistration();
+
+  // Gets the full device description JSON object, or nullptr if
+  // the device is not registered or communication failure.
+  std::unique_ptr<base::Value> GetDeviceInfo();
+
+  // Starts device registration procedure. |params| are a list of
+  // key-value pairs of device information, such as client_id, client_secret,
+  // and so on. If a particular key-value pair is omitted, a default value
+  // is used when possible. Returns a device claim ID on success.
+  std::string StartRegistration(
+    const std::map<std::string, std::shared_ptr<base::Value>>& params,
+    std::string* error_msg);
+
+  // Finalizes the device registration. If |user_auth_code| is provided, then
+  // the device record is populated with user email on user's behalf. Otherwise
+  // the user is responsible to issue a PATCH request to provide a valid
+  // email address before calling FinishRegistration.
+  bool FinishRegistration(const std::string& user_auth_code);
+
+ private:
+  // Saves the device registration to cache.
+  bool Save() const;
+
+  // Makes sure the access token is available and up-to-date.
+  bool ValidateAndRefreshAccessToken();
+
+  // Persistent data. Some of default values for testing purposes are used.
+  // TODO(avakulenko): remove these default values in the future.
+  // http://crbug.com/364692
+  std::string client_id_ =
+    "583509257718-lnmeofvjef3b1tm33sbjmckfnumfvn8j.apps.googleusercontent.com";
+  std::string client_secret_ = "6fzZwQhgnsHhvYYvvFdpv5SD";
+  std::string api_key_ = "AIzaSyAp7KVig5m9g4LWWKr79mTS8sXWfUU6w9g";
+  std::string refresh_token_;
+  std::string device_id_;
+  std::string device_robot_account_;
+  std::string oauth_url_ = "https://accounts.google.com/o/oauth2/";
+  std::string service_url_ =
+    "https://www-googleapis-staging.sandbox.google.com/"
+    "clouddevices/v1/";
+
+  // Transient data
+  std::string access_token_;
+  base::Time access_token_expiration_;
+  std::string ticket_id_;
+  std::string device_kind_ = "vendor";
+  std::string system_name_ = "coffee_pot";
+  std::string display_name_ = "Coffee Pot";
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceRegistrationInfo);
+};
+
+}  // namespace buffet
+
+#endif  // BUFFET_DEVICE_INFO_H_
diff --git a/buffet/http_transport_curl.cc b/buffet/http_transport_curl.cc
index 2a9c228..be204d9 100644
--- a/buffet/http_transport_curl.cc
+++ b/buffet/http_transport_curl.cc
@@ -185,10 +185,10 @@
       curl_easy_setopt(curl_handle_, CURLOPT_CUSTOMREQUEST, method_.c_str());
   }
 
-  VLOG_IF(2, !request_data_.empty()) << "Request data ("
-      << request_data_.size() << "): "
+  LOG(INFO) << "Request data (" << request_data_.size() << ")";
+  VLOG_IF(2, !request_data_.empty()) << "Raw request data: "
       << std::string(reinterpret_cast<const char*>(request_data_.data()),
-                                                   request_data_.size());
+                     request_data_.size());
 
   // Setup HTTP response data.
   if (method_ != request_type::kHead) {
diff --git a/buffet/http_utils.cc b/buffet/http_utils.cc
index 7529796..8210221 100644
--- a/buffet/http_utils.cc
+++ b/buffet/http_utils.cc
@@ -16,13 +16,14 @@
 namespace chromeos {
 namespace http {
 
-std::unique_ptr<Response> Get(const std::string& url) {
-  Request request(url);
-  return request.GetResponse();
+std::unique_ptr<Response> Get(const std::string& url,
+                              const HeaderList& headers) {
+  return SendRequest(request_type::kGet, url, nullptr, 0, nullptr, headers);
 }
 
-std::string GetAsString(const std::string& url) {
-  auto resp = Get(url);
+std::string GetAsString(const std::string& url,
+                        const HeaderList& headers) {
+  auto resp = Get(url, headers);
   return resp ? resp->GetDataAsString() : std::string();
 }
 
diff --git a/buffet/http_utils.h b/buffet/http_utils.h
index 91e12b5..2979ed3 100644
--- a/buffet/http_utils.h
+++ b/buffet/http_utils.h
@@ -37,12 +37,19 @@
                                       const HeaderList& headers);
 
 // Performs a simple GET request and returns the data as a string.
-std::string GetAsString(const std::string& url);
+std::string GetAsString(const std::string& url, const HeaderList& headers);
+inline std::string GetAsString(const std::string& url) {
+  return GetAsString(url, HeaderList());
+}
 
 // Performs a GET request. Success status, returned data and additional
 // information (such as returned HTTP headers) can be obtained from
 // the returned Response object.
-std::unique_ptr<Response> Get(const std::string& url);
+std::unique_ptr<Response> Get(const std::string& url,
+                              const HeaderList& headers);
+inline std::unique_ptr<Response> Get(const std::string& url) {
+  return Get(url, HeaderList());
+}
 
 // Performs a HEAD request. Success status and additional
 // information (such as returned HTTP headers) can be obtained from
diff --git a/buffet/manager.cc b/buffet/manager.cc
index b23d639..a6c8d9f 100644
--- a/buffet/manager.cc
+++ b/buffet/manager.cc
@@ -7,6 +7,8 @@
 #include <base/bind.h>
 #include <base/bind_helpers.h>
 #include <dbus/object_path.h>
+#include <dbus/values_util.h>
+#include <base/json/json_writer.h>
 
 #include "buffet/async_event_sequencer.h"
 #include "buffet/dbus_constants.h"
@@ -38,14 +40,47 @@
       new dbus_utils::AsyncEventSequencer());
   exported_object_->ExportMethod(
       dbus_constants::kManagerInterface,
-      dbus_constants::kManagerRegisterDeviceMethod,
+      dbus_constants::kManagerCheckDeviceRegistered,
       dbus_utils::GetExportableDBusMethod(
-          base::Bind(&Manager::HandleRegisterDevice,
+          base::Bind(&Manager::HandleCheckDeviceRegistered,
           base::Unretained(this))),
       sequencer->GetExportHandler(
           dbus_constants::kManagerInterface,
-          dbus_constants::kManagerRegisterDeviceMethod,
-          "Failed exporting RegisterDevice method",
+          dbus_constants::kManagerCheckDeviceRegistered,
+          "Failed exporting CheckDeviceRegistered method",
+          true));
+  exported_object_->ExportMethod(
+      dbus_constants::kManagerInterface,
+      dbus_constants::kManagerGetDeviceInfo,
+      dbus_utils::GetExportableDBusMethod(
+          base::Bind(&Manager::HandleGetDeviceInfo,
+          base::Unretained(this))),
+      sequencer->GetExportHandler(
+          dbus_constants::kManagerInterface,
+          dbus_constants::kManagerGetDeviceInfo,
+          "Failed exporting GetDeviceInfo method",
+          true));
+  exported_object_->ExportMethod(
+      dbus_constants::kManagerInterface,
+      dbus_constants::kManagerStartRegisterDevice,
+      dbus_utils::GetExportableDBusMethod(
+          base::Bind(&Manager::HandleStartRegisterDevice,
+          base::Unretained(this))),
+      sequencer->GetExportHandler(
+          dbus_constants::kManagerInterface,
+          dbus_constants::kManagerStartRegisterDevice,
+          "Failed exporting StartRegisterDevice method",
+          true));
+  exported_object_->ExportMethod(
+      dbus_constants::kManagerInterface,
+      dbus_constants::kManagerFinishRegisterDevice,
+      dbus_utils::GetExportableDBusMethod(
+          base::Bind(&Manager::HandleFinishRegisterDevice,
+          base::Unretained(this))),
+      sequencer->GetExportHandler(
+          dbus_constants::kManagerInterface,
+          dbus_constants::kManagerFinishRegisterDevice,
+          "Failed exporting FinishRegisterDevice method",
           true));
   exported_object_->ExportMethod(
       dbus_constants::kManagerInterface,
@@ -65,38 +100,122 @@
   properties_->Init(
       sequencer->GetHandler("Manager properties export failed.", true));
   sequencer->OnAllTasksCompletedCall({cb});
+  device_info_.Load();
 }
 
-scoped_ptr<dbus::Response> Manager::HandleRegisterDevice(
+scoped_ptr<dbus::Response> Manager::HandleCheckDeviceRegistered(
     dbus::MethodCall* method_call) {
   // Read the parameters to the method.
   dbus::MessageReader reader(method_call);
-  if (!reader.HasMoreData()) {
-    return GetBadArgsError(method_call, "No parameters to RegisterDevice");
-  }
-  std::string client_id, client_secret, api_key;
-  if (!reader.PopString(&client_id)) {
-    return GetBadArgsError(method_call, "Failed to read client_id");
-  }
-  if (!reader.PopString(&client_secret)) {
-    return GetBadArgsError(method_call, "Failed to read client_secret");
-  }
-  if (!reader.PopString(&api_key)) {
-    return GetBadArgsError(method_call, "Failed to read api_key");
-  }
   if (reader.HasMoreData()) {
-    return GetBadArgsError(
-        method_call, "Too many parameters to RegisterDevice");
+    return GetBadArgsError(method_call,
+                           "Too many parameters to CheckDeviceRegistered");
   }
 
-  LOG(INFO) << "Received call to Manager.RegisterDevice()";
-  // TODO(wiley): Do something with these parameters to register the device.
+  LOG(INFO) << "Received call to Manager.CheckDeviceRegistered()";
+
+  bool registered = device_info_.CheckRegistration();
 
   // Send back our response.
   scoped_ptr<dbus::Response> response(
       dbus::Response::FromMethodCall(method_call));
   dbus::MessageWriter writer(response.get());
-  writer.AppendString("<registration ticket id>");
+  writer.AppendString(registered ? device_info_.GetDeviceId() : std::string());
+  return response.Pass();
+}
+
+scoped_ptr<dbus::Response> Manager::HandleGetDeviceInfo(
+    dbus::MethodCall* method_call) {
+  // Read the parameters to the method.
+  dbus::MessageReader reader(method_call);
+  if (reader.HasMoreData()) {
+    return GetBadArgsError(method_call,
+                           "Too many parameters to GetDeviceInfo");
+  }
+
+  LOG(INFO) << "Received call to Manager.GetDeviceInfo()";
+
+  std::string device_info_str;
+  std::unique_ptr<base::Value> device_info = device_info_.GetDeviceInfo();
+  if (device_info)
+    base::JSONWriter::Write(device_info.get(), &device_info_str);
+
+  // Send back our response.
+  scoped_ptr<dbus::Response> response(
+      dbus::Response::FromMethodCall(method_call));
+  dbus::MessageWriter writer(response.get());
+  writer.AppendString(device_info_str);
+  return response.Pass();
+}
+
+scoped_ptr<dbus::Response> Manager::HandleStartRegisterDevice(
+    dbus::MethodCall* method_call) {
+  // Read the parameters to the method.
+  dbus::MessageReader reader(method_call);
+  if (!reader.HasMoreData()) {
+    return GetBadArgsError(method_call, "No parameters to StartRegisterDevice");
+  }
+
+  dbus::MessageReader array_reader(nullptr);
+  if (!reader.PopArray(&array_reader))
+    return GetBadArgsError(method_call, "Failed to read the parameter array");
+  std::map<std::string, std::shared_ptr<base::Value>> params;
+  while (array_reader.HasMoreData()) {
+    dbus::MessageReader dict_entry_reader(nullptr);
+    if (!array_reader.PopDictEntry(&dict_entry_reader))
+      return GetBadArgsError(method_call, "Failed to get a call parameter");
+    std::string key;
+    if (!dict_entry_reader.PopString(&key))
+      return GetBadArgsError(method_call, "Failed to read parameter key");
+    base::Value* value = dbus::PopDataAsValue(&dict_entry_reader);
+    if (!value)
+      return GetBadArgsError(method_call, "Failed to read parameter value");
+    params.insert(std::make_pair(key, std::shared_ptr<base::Value>(value)));
+  }
+  if (reader.HasMoreData())
+    return GetBadArgsError(method_call,
+                           "Too many parameters to StartRegisterDevice");
+
+  LOG(INFO) << "Received call to Manager.StartRegisterDevice()";
+
+  std::string error_msg;
+  std::string id = device_info_.StartRegistration(params, &error_msg);
+  if(id.empty())
+    return GetBadArgsError(method_call, error_msg);
+
+  // Send back our response.
+  scoped_ptr<dbus::Response> response(
+      dbus::Response::FromMethodCall(method_call));
+  dbus::MessageWriter writer(response.get());
+  writer.AppendString(id);
+  return response.Pass();
+}
+
+scoped_ptr<dbus::Response> Manager::HandleFinishRegisterDevice(
+  dbus::MethodCall* method_call) {
+  // Read the parameters to the method.
+  dbus::MessageReader reader(method_call);
+  if (!reader.HasMoreData()) {
+    return GetBadArgsError(method_call,
+                           "No parameters to FinishRegisterDevice");
+  }
+  std::string user_auth_code;
+  if (!reader.PopString(&user_auth_code)) {
+    return GetBadArgsError(method_call, "Failed to read UserAuthCode");
+  }
+  if (reader.HasMoreData()) {
+    return GetBadArgsError(method_call,
+                           "Too many parameters to FinishRegisterDevice");
+  }
+
+  LOG(INFO) << "Received call to Manager.FinishRegisterDevice()";
+  bool success = device_info_.FinishRegistration(user_auth_code);
+
+  // Send back our response.
+  scoped_ptr<dbus::Response> response(
+    dbus::Response::FromMethodCall(method_call));
+  dbus::MessageWriter writer(response.get());
+  writer.AppendString(success ? device_info_.GetDeviceId() : std::string());
   return response.Pass();
 }
 
diff --git a/buffet/manager.h b/buffet/manager.h
index 95d0548..ad66252 100644
--- a/buffet/manager.h
+++ b/buffet/manager.h
@@ -7,11 +7,14 @@
 
 #include <base/basictypes.h>
 #include <base/memory/scoped_ptr.h>
+#include <base/values.h>
 #include <dbus/message.h>
 #include <dbus/object_path.h>
+#include <memory>
 
 #include "buffet/dbus_constants.h"
 #include "buffet/exported_property_set.h"
+#include "buffet/device_registration_info.h"
 
 namespace buffet {
 
@@ -40,8 +43,17 @@
     virtual ~Properties() {}
   };
 
-  // Handles calls to org.chromium.Buffet.Manager.RegisterDevice().
-  scoped_ptr<dbus::Response> HandleRegisterDevice(
+  // Handles calls to org.chromium.Buffet.Manager.CheckDeviceRegistered().
+  scoped_ptr<dbus::Response> HandleCheckDeviceRegistered(
+      dbus::MethodCall* method_call);
+  // Handles calls to org.chromium.Buffet.Manager.GetDeviceInfo().
+  scoped_ptr<dbus::Response> HandleGetDeviceInfo(
+      dbus::MethodCall* method_call);
+  // Handles calls to org.chromium.Buffet.Manager.StartRegisterDevice().
+  scoped_ptr<dbus::Response> HandleStartRegisterDevice(
+      dbus::MethodCall* method_call);
+  // Handles calls to org.chromium.Buffet.Manager.FinishRegisterDevice().
+  scoped_ptr<dbus::Response> HandleFinishRegisterDevice(
       dbus::MethodCall* method_call);
   // Handles calls to org.chromium.Buffet.Manager.UpdateState().
   scoped_ptr<dbus::Response> HandleUpdateState(
@@ -51,6 +63,8 @@
   dbus::ExportedObject* exported_object_;  // weak; owned by the Bus object.
   scoped_ptr<Properties> properties_;
 
+  DeviceRegistrationInfo device_info_;
+
   DISALLOW_COPY_AND_ASSIGN(Manager);
 };