Merge "Prevent fetch command queue requests from piling up"
diff --git a/libweave/src/device_registration_info.cc b/libweave/src/device_registration_info.cc
index 2f058a8..310eaf0 100644
--- a/libweave/src/device_registration_info.cc
+++ b/libweave/src/device_registration_info.cc
@@ -1008,11 +1008,10 @@
   StartQueuedUpdateDeviceResource();
 }
 
-namespace {
-
-void HandleFetchCommandsResult(
+void DeviceRegistrationInfo::OnFetchCommandsSuccess(
     const base::Callback<void(const base::ListValue&)>& callback,
     const base::DictionaryValue& json) {
+  OnFetchCommandsReturned();
   const base::ListValue* commands{nullptr};
   if (!json.GetList("commands", &commands)) {
     VLOG(2) << "No commands in the response.";
@@ -1021,18 +1020,41 @@
   callback.Run(commands ? *commands : empty);
 }
 
-}  // namespace
+void DeviceRegistrationInfo::OnFetchCommandsError(
+    const CloudRequestErrorCallback& callback,
+    const Error* error) {
+  OnFetchCommandsReturned();
+  callback.Run(error);
+}
+
+void DeviceRegistrationInfo::OnFetchCommandsReturned() {
+  fetch_commands_request_sent_ = false;
+  // If we have additional requests queued, send them out now.
+  if (fetch_commands_request_queued_)
+    FetchAndPublishCommands();
+}
 
 void DeviceRegistrationInfo::FetchCommands(
     const base::Callback<void(const base::ListValue&)>& on_success,
     const CloudRequestErrorCallback& on_failure) {
+  fetch_commands_request_sent_ = true;
+  fetch_commands_request_queued_ = false;
   DoCloudRequest(
       http::kGet,
       GetServiceURL("commands/queue", {{"deviceId", config_->device_id()}}),
-      nullptr, base::Bind(&HandleFetchCommandsResult, on_success), on_failure);
+      nullptr,
+      base::Bind(&DeviceRegistrationInfo::OnFetchCommandsSuccess, AsWeakPtr(),
+                 on_success),
+      base::Bind(&DeviceRegistrationInfo::OnFetchCommandsError, AsWeakPtr(),
+                 on_failure));
 }
 
 void DeviceRegistrationInfo::FetchAndPublishCommands() {
+  if (fetch_commands_request_sent_) {
+    fetch_commands_request_queued_ = true;
+    return;
+  }
+
   FetchCommands(base::Bind(&DeviceRegistrationInfo::PublishCommands,
                            weak_factory_.GetWeakPtr()),
                 base::Bind(&IgnoreCloudError));
diff --git a/libweave/src/device_registration_info.h b/libweave/src/device_registration_info.h
index 564fb87..93fe85c 100644
--- a/libweave/src/device_registration_info.h
+++ b/libweave/src/device_registration_info.h
@@ -233,6 +233,15 @@
   void FetchCommands(
       const base::Callback<void(const base::ListValue&)>& on_success,
       const CloudRequestErrorCallback& on_failure);
+  // Success/failure callbacks for FetchCommands().
+  void OnFetchCommandsSuccess(
+      const base::Callback<void(const base::ListValue&)>& callback,
+      const base::DictionaryValue& json);
+  void OnFetchCommandsError(const CloudRequestErrorCallback& callback,
+                            const Error* error);
+  // Called when FetchCommands completes (with either success or error).
+  // This method reschedules any pending/queued fetch requests.
+  void OnFetchCommandsReturned();
 
   // Processes the command list that is fetched from the server on connection.
   // Aborts commands which are in transitional states and publishes queued
@@ -307,6 +316,12 @@
   // to the cloud server.
   bool device_state_update_pending_{false};
 
+  // Set to true when command queue fetch request is in flight to the server.
+  bool fetch_commands_request_sent_{false};
+  // Set to true when another command queue fetch request is queued while
+  // another one was in flight.
+  bool fetch_commands_request_queued_{false};
+
   using ResourceUpdateCallbackList =
       std::vector<std::pair<base::Closure, CloudRequestErrorCallback>>;
   // Success/error callbacks for device resource update request currently in