Prevent fetch command queue requests from piling up

Once FetchCommands request is sent, it will be retried automatically
upon network failures. While existing request is in flight, do not
sent out another ones (which might be initiated from the periodic
polls when XMPP channel is not available).

BUG: 23970412
Change-Id: Iaead29c86bb8661a71833a409cda06953df4ab0a
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