Call command handler with weak_ptr

Command handler should keep only weak_ptr to command and check on every
access. Long async commands will need to keep reference to the command.

BUG:24267885

Change-Id: I3ca3be9e31b9e9a942eca001ed21f1133973f0ea
Reviewed-on: https://weave-review.googlesource.com/1255
Reviewed-by: Alex Vakulenko <avakulenko@google.com>
diff --git a/libweave/examples/ubuntu/main.cc b/libweave/examples/ubuntu/main.cc
index a116178..28611f5 100644
--- a/libweave/examples/ubuntu/main.cc
+++ b/libweave/examples/ubuntu/main.cc
@@ -33,12 +33,26 @@
 
 class CommandHandler {
  public:
-  explicit CommandHandler(weave::Device* device) : device_{device} {
+  CommandHandler(weave::Device* device,
+                 weave::provider::TaskRunner* task_runner)
+      : device_{device}, task_runner_(task_runner) {
+    device->AddStateDefinitionsFromJson(R"({
+      "_greeter": {"_greetings_counter":"integer"},
+      "_ledflasher": {"_leds": {"items": "boolean"}}
+    })");
+
+    device->SetStatePropertiesFromJson(R"({
+      "_greeter": {"_greetings_counter": 0},
+      "_ledflasher":{"_leds": [false, false, false]}
+    })",
+                                       nullptr);
+
     device->AddCommandDefinitionsFromJson(R"({
       "_greeter": {
         "_greet": {
           "minimalRole": "user",
-          "parameters": { "_name": "string"},
+          "parameters": { "_name": "string", "_count": "integer"},
+          "progress": { "_todo": "integer"},
           "results": { "_greeting": "string" }
         }
       },
@@ -56,58 +70,76 @@
         }
       }
     })");
-
-    device->AddStateDefinitionsFromJson(R"({
-      "_greeter": {"_greetings_counter":"integer"},
-      "_ledflasher": {"_leds": {"items": "boolean"}}
-    })");
-
-    device->SetStatePropertiesFromJson(R"({
-      "_greeter": {"_greetings_counter": 0},
-      "_ledflasher":{"_leds": [false, false, false]}
-    })",
-                                       nullptr);
-
+    device->AddCommandHandler(
+        "_ledflasher._toggle",
+        base::Bind(&CommandHandler::OnFlasherToggleCommand,
+                   weak_ptr_factory_.GetWeakPtr()));
     device->AddCommandHandler("_greeter._greet",
                               base::Bind(&CommandHandler::OnGreetCommand,
                                          weak_ptr_factory_.GetWeakPtr()));
     device->AddCommandHandler("_ledflasher._set",
                               base::Bind(&CommandHandler::OnFlasherSetCommand,
                                          weak_ptr_factory_.GetWeakPtr()));
-    device->AddCommandHandler(
-        "_ledflasher._toggle",
-        base::Bind(&CommandHandler::OnFlasherToggleCommand,
-                   weak_ptr_factory_.GetWeakPtr()));
     device->AddCommandHandler("",
                               base::Bind(&CommandHandler::OnUnhandledCommand,
                                          weak_ptr_factory_.GetWeakPtr()));
   }
 
  private:
-  void OnGreetCommand(weave::Command* cmd) {
-    LOG(INFO) << "received command: " << cmd->GetName();
+  void DoGreet(const std::weak_ptr<weave::Command>& command, int todo) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
+
     std::string name;
     if (!cmd->GetParameters()->GetString("_name", &name))
       name = "anonymous";
 
-    LOG(INFO) << cmd->GetName() << " command in progress";
-    cmd->SetProgress(base::DictionaryValue{}, nullptr);
+    if (todo > 0) {
+      LOG(INFO) << "Hello " << name;
+
+      base::DictionaryValue progress;
+      progress.SetInteger("_todo", todo);
+      cmd->SetProgress(progress, nullptr);
+
+      base::DictionaryValue state;
+      state.SetInteger("_greeter._greetings_counter", ++counter_);
+      device_->SetStateProperties(state, nullptr);
+    }
+
+    if (--todo > 0) {
+      task_runner_->PostDelayedTask(
+          FROM_HERE, base::Bind(&CommandHandler::DoGreet,
+                                weak_ptr_factory_.GetWeakPtr(), command, todo),
+          base::TimeDelta::FromSeconds(1));
+      return;
+    }
 
     base::DictionaryValue result;
     result.SetString("_greeting", "Hello " + name);
     cmd->SetResults(result, nullptr);
     LOG(INFO) << cmd->GetName() << " command finished: " << result;
 
-    base::DictionaryValue state;
-    state.SetInteger("_greeter._greetings_counter", ++counter_);
-    device_->SetStateProperties(state, nullptr);
-
     LOG(INFO) << "New state: " << *device_->GetState();
 
     cmd->Done();
   }
 
-  void OnFlasherSetCommand(weave::Command* cmd) {
+  void OnGreetCommand(const std::weak_ptr<weave::Command>& command) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
+    LOG(INFO) << "received command: " << cmd->GetName();
+
+    int todo = 1;
+    cmd->GetParameters()->GetInteger("_count", &todo);
+    DoGreet(command, todo);
+  }
+
+  void OnFlasherSetCommand(const std::weak_ptr<weave::Command>& command) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
     LOG(INFO) << "received command: " << cmd->GetName();
     int32_t led_index;
     bool cmd_value;
@@ -130,7 +162,10 @@
     cmd->Abort();
   }
 
-  void OnFlasherToggleCommand(weave::Command* cmd) {
+  void OnFlasherToggleCommand(const std::weak_ptr<weave::Command>& command) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
     LOG(INFO) << "received command: " << cmd->GetName();
     int32_t led_index;
     if (cmd->GetParameters()->GetInteger("_led", &led_index)) {
@@ -144,7 +179,10 @@
     cmd->Abort();
   }
 
-  void OnUnhandledCommand(weave::Command* cmd) {
+  void OnUnhandledCommand(const std::weak_ptr<weave::Command>& command) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
     LOG(INFO) << cmd->GetName() << " unimplemented command: ignored";
   }
 
@@ -157,6 +195,8 @@
   }
 
   weave::Device* device_{nullptr};
+  weave::provider::TaskRunner* task_runner_{nullptr};
+
   int counter_{0};
 
   // Simulate LED status on this device so client app could explore
@@ -218,7 +258,7 @@
     }
   }
 
-  CommandHandler handler(device.get());
+  CommandHandler handler(device.get(), &task_runner);
   task_runner.Run();
 
   LOG(INFO) << "exit";
diff --git a/libweave/include/weave/command.h b/libweave/include/weave/command.h
index 911da1e..6c399e1 100644
--- a/libweave/include/weave/command.h
+++ b/libweave/include/weave/command.h
@@ -27,25 +27,6 @@
 
 class Command {
  public:
-  // This interface lets the command to notify clients about changes.
-  class Observer {
-   public:
-    virtual void OnResultsChanged() = 0;
-    virtual void OnStatusChanged() = 0;
-    virtual void OnProgressChanged() = 0;
-    virtual void OnCommandDestroyed() = 0;
-
-   protected:
-    virtual ~Observer() = default;
-  };
-
-  // Adds an observer for this command. The observer object is not owned by this
-  // class.
-  virtual void AddObserver(Observer* observer) = 0;
-
-  // Removes an observer for this command.
-  virtual void RemoveObserver(Observer* observer) = 0;
-
   // Returns the full command ID.
   virtual const std::string& GetID() const = 0;
 
diff --git a/libweave/include/weave/device.h b/libweave/include/weave/device.h
index 289cedb..7e49202 100644
--- a/libweave/include/weave/device.h
+++ b/libweave/include/weave/device.h
@@ -52,7 +52,8 @@
   virtual void AddCommandDefinitions(const base::DictionaryValue& dict) = 0;
 
   // Callback type for AddCommandHandler.
-  using CommandHandlerCallback = base::Callback<void(Command*)>;
+  using CommandHandlerCallback =
+      base::Callback<void(const std::weak_ptr<Command>& command)>;
 
   // Sets handler for new commands added to the queue.
   // |command_name| is the full command name of the command to handle. e.g.
diff --git a/libweave/include/weave/test/mock_command.h b/libweave/include/weave/test/mock_command.h
index be1b0c3..b856140 100644
--- a/libweave/include/weave/test/mock_command.h
+++ b/libweave/include/weave/test/mock_command.h
@@ -20,8 +20,6 @@
  public:
   ~MockCommand() override = default;
 
-  MOCK_METHOD1(AddObserver, void(Observer*));
-  MOCK_METHOD1(RemoveObserver, void(Observer*));
   MOCK_CONST_METHOD0(GetID, const std::string&());
   MOCK_CONST_METHOD0(GetName, const std::string&());
   MOCK_CONST_METHOD0(GetCategory, const std::string&());
diff --git a/libweave/src/base_api_handler.cc b/libweave/src/base_api_handler.cc
index 1a0a58d..dad443f 100644
--- a/libweave/src/base_api_handler.cc
+++ b/libweave/src/base_api_handler.cc
@@ -46,7 +46,11 @@
                                         weak_ptr_factory_.GetWeakPtr()));
 }
 
-void BaseApiHandler::UpdateBaseConfiguration(Command* command) {
+void BaseApiHandler::UpdateBaseConfiguration(
+    const std::weak_ptr<Command>& cmd) {
+  auto command = cmd.lock();
+  if (!command)
+    return;
   CHECK(command->GetStatus() == CommandStatus::kQueued)
       << EnumToString(command->GetStatus());
   command->SetProgress(base::DictionaryValue{}, nullptr);
@@ -83,7 +87,10 @@
   device_->SetStateProperties(state, nullptr);
 }
 
-void BaseApiHandler::UpdateDeviceInfo(Command* command) {
+void BaseApiHandler::UpdateDeviceInfo(const std::weak_ptr<Command>& cmd) {
+  auto command = cmd.lock();
+  if (!command)
+    return;
   CHECK(command->GetStatus() == CommandStatus::kQueued)
       << EnumToString(command->GetStatus());
   command->SetProgress(base::DictionaryValue{}, nullptr);
diff --git a/libweave/src/base_api_handler.h b/libweave/src/base_api_handler.h
index 7712de1..c690b03 100644
--- a/libweave/src/base_api_handler.h
+++ b/libweave/src/base_api_handler.h
@@ -28,8 +28,8 @@
   BaseApiHandler(DeviceRegistrationInfo* device_info, Device* device);
 
  private:
-  void UpdateBaseConfiguration(Command* command);
-  void UpdateDeviceInfo(Command* command);
+  void UpdateBaseConfiguration(const std::weak_ptr<Command>& command);
+  void UpdateDeviceInfo(const std::weak_ptr<Command>& command);
   bool UpdateState(const std::string& anonymous_access_role,
                    bool discovery_enabled,
                    bool pairing_enabled);
diff --git a/libweave/src/commands/cloud_command_proxy.h b/libweave/src/commands/cloud_command_proxy.h
index c5be02e..711befa 100644
--- a/libweave/src/commands/cloud_command_proxy.h
+++ b/libweave/src/commands/cloud_command_proxy.h
@@ -17,6 +17,7 @@
 
 #include "src/backoff_entry.h"
 #include "src/commands/cloud_command_update_interface.h"
+#include "src/commands/command_instance.h"
 #include "src/states/state_change_queue_interface.h"
 
 namespace weave {
@@ -28,7 +29,7 @@
 }
 
 // Command proxy which publishes command updates to the cloud.
-class CloudCommandProxy final : public Command::Observer {
+class CloudCommandProxy final : public CommandInstance::Observer {
  public:
   CloudCommandProxy(CommandInstance* command_instance,
                     CloudCommandUpdateInterface* cloud_command_updater,
@@ -93,7 +94,7 @@
   // successfully.
   UpdateID last_state_update_id_{0};
 
-  ScopedObserver<Command, Command::Observer> observer_{this};
+  ScopedObserver<CommandInstance, CommandInstance::Observer> observer_{this};
 
   base::WeakPtrFactory<CloudCommandProxy> backoff_weak_ptr_factory_{this};
   base::WeakPtrFactory<CloudCommandProxy> weak_ptr_factory_{this};
diff --git a/libweave/src/commands/command_instance.cc b/libweave/src/commands/command_instance.cc
index a8891f7..305a255 100644
--- a/libweave/src/commands/command_instance.cc
+++ b/libweave/src/commands/command_instance.cc
@@ -37,6 +37,12 @@
     {CommandOrigin::kCloud, "cloud"},
 };
 
+void ReportDestroyedError(ErrorPtr* error) {
+  Error::AddTo(error, FROM_HERE, errors::commands::kDomain,
+               errors::commands::kCommandDestroyed,
+               "Command has been destroyed");
+}
+
 }  // namespace
 
 template <>
@@ -92,6 +98,10 @@
 
 bool CommandInstance::SetProgress(const base::DictionaryValue& progress,
                                   ErrorPtr* error) {
+  if (!command_definition_) {
+    ReportDestroyedError(error);
+    return false;
+  }
   ObjectPropType obj_prop_type;
   obj_prop_type.SetObjectSchema(command_definition_->GetProgress()->Clone());
 
@@ -110,6 +120,10 @@
 
 bool CommandInstance::SetResults(const base::DictionaryValue& results,
                                  ErrorPtr* error) {
+  if (!command_definition_) {
+    ReportDestroyedError(error);
+    return false;
+  }
   ObjectPropType obj_prop_type;
   obj_prop_type.SetObjectSchema(command_definition_->GetResults()->Clone());
 
diff --git a/libweave/src/commands/command_instance.h b/libweave/src/commands/command_instance.h
index 8a54b05..20e1daa 100644
--- a/libweave/src/commands/command_instance.h
+++ b/libweave/src/commands/command_instance.h
@@ -31,6 +31,17 @@
 
 class CommandInstance final : public Command {
  public:
+  class Observer {
+   public:
+    virtual void OnResultsChanged() = 0;
+    virtual void OnStatusChanged() = 0;
+    virtual void OnProgressChanged() = 0;
+    virtual void OnCommandDestroyed() = 0;
+
+   protected:
+    virtual ~Observer() = default;
+  };
+
   // Construct a command instance given the full command |name| which must
   // be in format "<package_name>.<command_name>", a command |category| and
   // a list of parameters and their values specified in |parameters|.
@@ -42,8 +53,6 @@
 
   // Command overrides.
   std::unique_ptr<base::DictionaryValue> ToJson() const override;
-  void AddObserver(Observer* observer) override;
-  void RemoveObserver(Observer* observer) override;
   const std::string& GetID() const override;
   const std::string& GetName() const override;
   CommandStatus GetStatus() const override;
@@ -82,8 +91,17 @@
   // Sets the command ID (normally done by CommandQueue when the command
   // instance is added to it).
   void SetID(const std::string& id) { id_ = id; }
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
   // Sets the pointer to queue this command is part of.
-  void SetCommandQueue(CommandQueue* queue) { queue_ = queue; }
+  void AttachToQueue(CommandQueue* queue) { queue_ = queue; }
+  void DetachFromQueue() {
+    observers_.Clear();
+    queue_ = nullptr;
+    command_definition_ = nullptr;
+  }
 
  private:
   // Helper function to update the command status.
@@ -101,7 +119,7 @@
   // The origin of the command, either "local" or "cloud".
   CommandOrigin origin_ = CommandOrigin::kLocal;
   // Command definition.
-  const CommandDefinition* command_definition_;
+  const CommandDefinition* command_definition_{nullptr};
   // Command parameters and their values.
   ValueMap parameters_;
   // Current command execution progress.
diff --git a/libweave/src/commands/command_manager.cc b/libweave/src/commands/command_manager.cc
index efca5ff..22d00dc 100644
--- a/libweave/src/commands/command_manager.cc
+++ b/libweave/src/commands/command_manager.cc
@@ -182,7 +182,7 @@
 
 void CommandManager::AddCommandHandler(
     const std::string& command_name,
-    const CommandQueue::CommandCallback& callback) {
+    const Device::CommandHandlerCallback& callback) {
   CHECK(command_name.empty() || base_dictionary_.FindCommand(command_name))
       << "Command undefined: " << command_name;
   command_queue_.AddCommandHandler(command_name, callback);
diff --git a/libweave/src/commands/command_manager.h b/libweave/src/commands/command_manager.h
index f34a907..fe3cb71 100644
--- a/libweave/src/commands/command_manager.h
+++ b/libweave/src/commands/command_manager.h
@@ -36,7 +36,7 @@
   void AddCommandAddedCallback(const CommandQueue::CommandCallback& callback);
   void AddCommandRemovedCallback(const CommandQueue::CommandCallback& callback);
   void AddCommandHandler(const std::string& command_name,
-                         const CommandQueue::CommandCallback& callback);
+                         const Device::CommandHandlerCallback& callback);
 
   // Sets callback which is called when command definitions is changed.
   void AddCommandDefChanged(const base::Closure& callback);
diff --git a/libweave/src/commands/command_queue.cc b/libweave/src/commands/command_queue.cc
index df86d77..efdcfdc 100644
--- a/libweave/src/commands/command_queue.cc
+++ b/libweave/src/commands/command_queue.cc
@@ -24,8 +24,9 @@
   on_command_removed_.push_back(callback);
 }
 
-void CommandQueue::AddCommandHandler(const std::string& command_name,
-                                     const CommandCallback& callback) {
+void CommandQueue::AddCommandHandler(
+    const std::string& command_name,
+    const Device::CommandHandlerCallback& callback) {
   if (!command_name.empty()) {
     CHECK(default_command_callback_.is_null())
         << "Commands specific handler are not allowed after default one";
@@ -33,7 +34,7 @@
     for (const auto& command : map_) {
       if (command.second->GetStatus() == CommandStatus::kQueued &&
           command.second->GetName() == command_name) {
-        callback.Run(command.second.get());
+        callback.Run(command.second);
       }
     }
 
@@ -45,7 +46,7 @@
       if (command.second->GetStatus() == CommandStatus::kQueued &&
           command_callbacks_.find(command.second->GetName()) ==
               command_callbacks_.end()) {
-        callback.Run(command.second.get());
+        callback.Run(command.second);
       }
     }
 
@@ -57,7 +58,7 @@
 void CommandQueue::Add(std::unique_ptr<CommandInstance> instance) {
   std::string id = instance->GetID();
   LOG_IF(FATAL, id.empty()) << "Command has no ID";
-  instance->SetCommandQueue(this);
+  instance->AttachToQueue(this);
   auto pair = map_.insert(std::make_pair(id, std::move(instance)));
   LOG_IF(FATAL, !pair.second) << "Command with ID '" << id
                               << "' is already in the queue";
@@ -67,9 +68,9 @@
   auto it_handler = command_callbacks_.find(pair.first->second->GetName());
 
   if (it_handler != command_callbacks_.end())
-    it_handler->second.Run(pair.first->second.get());
+    it_handler->second.Run(pair.first->second);
   else if (!default_command_callback_.is_null())
-    default_command_callback_.Run(pair.first->second.get());
+    default_command_callback_.Run(pair.first->second);
 
   Cleanup();
 }
@@ -88,8 +89,8 @@
   auto p = map_.find(id);
   if (p == map_.end())
     return false;
-  std::unique_ptr<CommandInstance> instance{std::move(p->second)};
-  instance->SetCommandQueue(nullptr);
+  std::shared_ptr<CommandInstance> instance = p->second;
+  instance->DetachFromQueue();
   map_.erase(p);
   for (const auto& cb : on_command_removed_)
     cb.Run(instance.get());
diff --git a/libweave/src/commands/command_queue.h b/libweave/src/commands/command_queue.h
index 888c04f..99da0b2 100644
--- a/libweave/src/commands/command_queue.h
+++ b/libweave/src/commands/command_queue.h
@@ -25,7 +25,8 @@
  public:
   CommandQueue() = default;
 
-  using CommandCallback = Device::CommandHandlerCallback;
+  // TODO: Remove AddCommandAddedCallback and AddCommandRemovedCallback.
+  using CommandCallback = base::Callback<void(Command* command)>;
 
   // Adds notifications callback for a new command is added to the queue.
   void AddCommandAddedCallback(const CommandCallback& callback);
@@ -34,7 +35,7 @@
   void AddCommandRemovedCallback(const CommandCallback& callback);
 
   void AddCommandHandler(const std::string& command_name,
-                         const CommandCallback& callback);
+                         const Device::CommandHandlerCallback& callback);
 
   // Checks if the command queue is empty.
   bool IsEmpty() const { return map_.empty(); }
@@ -75,7 +76,7 @@
   base::Time test_now_;
 
   // ID-to-CommandInstance map.
-  std::map<std::string, std::unique_ptr<CommandInstance>> map_;
+  std::map<std::string, std::shared_ptr<CommandInstance>> map_;
 
   // Queue of commands to be removed.
   std::queue<std::pair<base::Time, std::string>> remove_queue_;
@@ -83,8 +84,8 @@
   using CallbackList = std::vector<CommandCallback>;
   CallbackList on_command_added_;
   CallbackList on_command_removed_;
-  std::map<std::string, CommandCallback> command_callbacks_;
-  CommandCallback default_command_callback_;
+  std::map<std::string, Device::CommandHandlerCallback> command_callbacks_;
+  Device::CommandHandlerCallback default_command_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(CommandQueue);
 };
diff --git a/libweave/src/commands/schema_constants.cc b/libweave/src/commands/schema_constants.cc
index 9affe73..02999f7 100644
--- a/libweave/src/commands/schema_constants.cc
+++ b/libweave/src/commands/schema_constants.cc
@@ -25,6 +25,7 @@
 const char kCommandFailed[] = "command_failed";
 const char kInvalidCommandVisibility[] = "invalid_command_visibility";
 const char kInvalidMinimalRole[] = "invalid_minimal_role";
+const char kCommandDestroyed[] = "command_destroyed";
 }  // namespace commands
 }  // namespace errors
 
diff --git a/libweave/src/commands/schema_constants.h b/libweave/src/commands/schema_constants.h
index 2d4ae07..9eb8051 100644
--- a/libweave/src/commands/schema_constants.h
+++ b/libweave/src/commands/schema_constants.h
@@ -28,6 +28,7 @@
 extern const char kCommandFailed[];
 extern const char kInvalidCommandVisibility[];
 extern const char kInvalidMinimalRole[];
+extern const char kCommandDestroyed[];
 }  // namespace commands
 }  // namespace errors