buffet: Serialize/queue device state updates to the cloud server

Make sure there are no simultaneous device state patche requests
sent to the GCD server. As long as one request is pending, postpone
all subsequent requests until the previous request finishes.

Since DoCloudRequest() performs all the necessary retries and transient
error handling, no additional error handling is required in
PublishStateUpdates() method.

BUG=brillo:1195
TEST=`FEATURES=test emerge-link buffet`
     `test_that -b link 100.96.49.59 e:buffet_.*`

Change-Id: Ia65f7c9f844580a01aaef89271373b8ca710ef19
Reviewed-on: https://chromium-review.googlesource.com/280861
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Vitaly Buka <vitalybuka@chromium.org>
Commit-Queue: Vitaly Buka <vitalybuka@chromium.org>
diff --git a/buffet/device_registration_info.cc b/buffet/device_registration_info.cc
index 00899ce..2d9fb80 100644
--- a/buffet/device_registration_info.cc
+++ b/buffet/device_registration_info.cc
@@ -887,7 +887,10 @@
 }
 
 void DeviceRegistrationInfo::PublishStateUpdates() {
-  VLOG(1) << "PublishStateUpdates";
+  // If we have pending state update requests, don't send any more for now.
+  if (device_state_update_pending_)
+    return;
+
   const std::vector<StateChange> state_changes{
       state_manager_->GetAndClearRecordedStateChanges()};
   if (state_changes.empty())
@@ -921,11 +924,24 @@
                  std::to_string(base::Time::Now().ToJavaTime()));
   body.Set("patches", patches.release());
 
+  device_state_update_pending_ = true;
   DoCloudRequest(
-      chromeos::http::request_type::kPost,
-      GetDeviceURL("patchState"),
-      &body,
-      base::Bind(&IgnoreCloudResult), base::Bind(&IgnoreCloudError));
+      chromeos::http::request_type::kPost, GetDeviceURL("patchState"), &body,
+      base::Bind(&DeviceRegistrationInfo::OnPublishStateSuccess, AsWeakPtr()),
+      base::Bind(&DeviceRegistrationInfo::OnPublishStateError, AsWeakPtr()));
+}
+
+void DeviceRegistrationInfo::OnPublishStateSuccess(
+    const base::DictionaryValue& reply) {
+  device_state_update_pending_ = false;
+  // See if there were more pending state updates since the previous request
+  // had been sent out.
+  PublishStateUpdates();
+}
+
+void DeviceRegistrationInfo::OnPublishStateError(const chromeos::Error* error) {
+  LOG(ERROR) << "Permanent failure while trying to update device state";
+  device_state_update_pending_ = false;
 }
 
 void DeviceRegistrationInfo::SetRegistrationStatus(