buffet: First step in device start implementation.

On device start up update cloud with the most recent view
of the device.

BUG=chromium:420573
TEST=cros_workon_make buffet --test and manual testing

Change-Id: I7925ea4b84185baf91dfa41b8f5f6c955c7b46ee
Reviewed-on: https://chromium-review.googlesource.com/221052
Tested-by: Anton Muhin <antonm@chromium.org>
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Anton Muhin <antonm@chromium.org>
Commit-Queue: Anton Muhin <antonm@chromium.org>
diff --git a/buffet/device_registration_info.cc b/buffet/device_registration_info.cc
index 925307d..1e4d511 100644
--- a/buffet/device_registration_info.cc
+++ b/buffet/device_registration_info.cc
@@ -307,6 +307,32 @@
   return true;
 }
 
+std::unique_ptr<base::DictionaryValue>
+DeviceRegistrationInfo::BuildDeviceResource(chromeos::ErrorPtr* error) {
+  std::unique_ptr<base::DictionaryValue> commands =
+      command_manager_->GetCommandDictionary().GetCommandsAsJson(true, error);
+  if (!commands)
+    return nullptr;
+
+  std::unique_ptr<base::DictionaryValue> state =
+      state_manager_->GetStateValuesAsJson(error);
+  if (!state)
+    return nullptr;
+
+  std::unique_ptr<base::DictionaryValue> resource{new base::DictionaryValue};
+  if (!device_id_.empty())
+    resource->SetString("id", device_id_);
+  resource->SetString("deviceKind", device_kind_);
+  resource->SetString("systemName", system_name_);
+  if (!display_name_.empty())
+    resource->SetString("displayName", display_name_);
+  resource->SetString("channel.supportedType", "xmpp");
+  resource->Set("commandDefs", commands.release());
+  resource->Set("state", state.release());
+
+  return resource;
+}
+
 std::unique_ptr<base::Value> DeviceRegistrationInfo::GetDeviceInfo(
     chromeos::ErrorPtr* error) {
   if (!CheckRegistration(error))
@@ -353,26 +379,16 @@
   GetParamValue(params, storage_keys::kOAuthURL, &oauth_url_);
   GetParamValue(params, storage_keys::kServiceURL, &service_url_);
 
-  std::unique_ptr<base::DictionaryValue> commands =
-      command_manager_->GetCommandDictionary().GetCommandsAsJson(true, error);
-  if (!commands)
-    return std::string();
 
-  std::unique_ptr<base::DictionaryValue> state =
-      state_manager_->GetStateValuesAsJson(error);
-  if (!state)
+  std::unique_ptr<base::DictionaryValue> device_draft =
+      BuildDeviceResource(error);
+  if (!device_draft)
     return std::string();
 
   base::DictionaryValue req_json;
   req_json.SetString("id", ticket_id_);
   req_json.SetString("oauthClientId", client_id_);
-  req_json.SetString("deviceDraft.deviceKind", device_kind_);
-  req_json.SetString("deviceDraft.systemName", system_name_);
-  if (!display_name_.empty())
-    req_json.SetString("deviceDraft.displayName", display_name_);
-  req_json.SetString("deviceDraft.channel.supportedType", "xmpp");
-  req_json.Set("deviceDraft.commandDefs", commands.release());
-  req_json.Set("deviceDraft.state", state.release());
+  req_json.Set("deviceDraft", device_draft.release());
 
   auto url = GetServiceURL("registrationTickets/" + ticket_id_,
                            {{"key", api_key_}});
@@ -467,4 +483,32 @@
   return true;
 }
 
+void DeviceRegistrationInfo::StartDevice(chromeos::ErrorPtr* error) {
+  if (!CheckRegistration(error))
+    return;
+
+  std::unique_ptr<base::DictionaryValue> device_resource =
+      BuildDeviceResource(error);
+  if (!device_resource)
+    return;
+
+  // TODO(antonm): Use PUT, not PATCH for updates.
+  std::unique_ptr<chromeos::http::Response> response =
+      chromeos::http::PatchJson(GetDeviceURL(), device_resource.release(),
+                                {GetAuthorizationHeader()}, transport_, error);
+  if (!response)
+    return;
+
+  int status_code = 0;
+  std::unique_ptr<base::DictionaryValue> json =
+      chromeos::http::ParseJsonResponse(response.get(), &status_code, error);
+  if (!json || status_code >= chromeos::http::status_code::BadRequest)
+    return;
+
+  // TODO(antonm): Implement the rest of startup sequence:
+  //   * Abort commands cloud thinks are running
+  //   * Poll for commands to run
+  //   * Schedule periodic polling
+}
+
 }  // namespace buffet
diff --git a/buffet/device_registration_info.h b/buffet/device_registration_info.h
index bd2159f..6a82ffa 100644
--- a/buffet/device_registration_info.h
+++ b/buffet/device_registration_info.h
@@ -107,6 +107,12 @@
   // StartRegistration must have been invoked before.
   bool FinishRegistration(chromeos::ErrorPtr* error);
 
+  // Start device execution.
+  // Device will do required start up chores and then start to listen
+  // to new commands.
+  // TODO(antonm): Consider moving into some other class.
+  void StartDevice(chromeos::ErrorPtr* error);
+
  private:
   // Saves the device registration to cache.
   bool Save() const;
@@ -114,6 +120,12 @@
   // Makes sure the access token is available and up-to-date.
   bool ValidateAndRefreshAccessToken(chromeos::ErrorPtr* error);
 
+  // Builds Cloud API devices collection REST resouce which matches
+  // current state of the device including command definitions
+  // for all supported commands and current device state.
+  std::unique_ptr<base::DictionaryValue> BuildDeviceResource(
+      chromeos::ErrorPtr* error);
+
   // Persistent data. Some of default values for testing purposes are used.
   // TODO(avakulenko): remove these default values in the future.
   // http://crbug.com/364692
diff --git a/buffet/manager.cc b/buffet/manager.cc
index 01716f4..af1632b 100644
--- a/buffet/manager.cc
+++ b/buffet/manager.cc
@@ -73,7 +73,8 @@
 
 void Manager::HandleStartDevice(chromeos::ErrorPtr* error) {
   LOG(INFO) << "Received call to Manager.StartDevice()";
-  LOG(INFO) << "Not implemented";
+
+  device_info_->StartDevice(error);
 }
 
 std::string Manager::HandleCheckDeviceRegistered(chromeos::ErrorPtr* error) {