Add trait/component APIs to weaved

Added new Device::AddComponent() method as well as added |component|
parameter to AddCommandHandler, SetStateProperty/SetStateProperties
methods. Added GetComponent() method to Command class.

Marked old APIs (that don't take component name) as deprecated.

Finally, made weaved load trait definitions from /etc/weaved/traits
directory.

BUG: 25917608, 25917704, 25916429, 25917604, 25917426, 25916428
Change-Id: I252f3930d1fda79e41c062d71f008210d2c116a4
diff --git a/buffet/dbus_bindings/com.android.Weave.Command.dbus-xml b/buffet/dbus_bindings/com.android.Weave.Command.dbus-xml
index 46913b3..3602135 100644
--- a/buffet/dbus_bindings/com.android.Weave.Command.dbus-xml
+++ b/buffet/dbus_bindings/com.android.Weave.Command.dbus-xml
@@ -33,6 +33,7 @@
     </method>
     <property name="Name" type="s" access="read"/>
     <property name="Id" type="s" access="read"/>
+    <property name="Component" type="s" access="read"/>
     <property name="State" type="s" access="read"/>
     <property name="Parameters" type="a{sv}" access="read"/>
     <property name="Progress" type="a{sv}" access="read"/>
diff --git a/buffet/dbus_bindings/com.android.Weave.Manager.dbus-xml b/buffet/dbus_bindings/com.android.Weave.Manager.dbus-xml
index a922491..ad26785 100644
--- a/buffet/dbus_bindings/com.android.Weave.Manager.dbus-xml
+++ b/buffet/dbus_bindings/com.android.Weave.Manager.dbus-xml
@@ -13,7 +13,13 @@
       <arg name="device_id" type="s" direction="out"/>
       <annotation name="org.chromium.DBus.Method.Kind" value="async"/>
     </method>
+    <method name="AddComponent">
+      <arg name="name" type="s" direction="in"/>
+      <arg name="traits" type="as" direction="in"/>
+      <annotation name="org.chromium.DBus.Method.Kind" value="async"/>
+    </method>
     <method name="UpdateState">
+      <arg name="component" type="s" direction="in"/>
       <arg name="property_set" type="a{sv}" direction="in"/>
       <annotation name="org.chromium.DBus.Method.Kind" value="async"/>
     </method>
diff --git a/buffet/dbus_command_proxy.cc b/buffet/dbus_command_proxy.cc
index b7e82c1..011ba01 100644
--- a/buffet/dbus_command_proxy.cc
+++ b/buffet/dbus_command_proxy.cc
@@ -62,6 +62,7 @@
   // Set the initial property values before registering the DBus object.
   dbus_adaptor_.SetName(command->GetName());
   dbus_adaptor_.SetId(command->GetID());
+  dbus_adaptor_.SetComponent(command->GetComponent());
   dbus_adaptor_.SetState(EnumToString(command->GetState()));
   dbus_adaptor_.SetProgress(
       DictionaryToDBusVariantDictionary(command->GetProgress()));
diff --git a/buffet/manager.cc b/buffet/manager.cc
index 61a5d7a..85a9024 100644
--- a/buffet/manager.cc
+++ b/buffet/manager.cc
@@ -80,6 +80,23 @@
   return true;
 }
 
+void LoadTraitDefinitions(const BuffetConfig::Options& options,
+                          weave::Device* device) {
+  // Load component-specific device trait definitions.
+  base::FilePath dir{options.definitions.Append("traits")};
+  LOG(INFO) << "Looking for trait definitions in " << dir.value();
+  base::FileEnumerator enumerator(dir, false, base::FileEnumerator::FILES,
+                                  FILE_PATH_LITERAL("*.json"));
+  std::vector<std::string> result;
+  for (base::FilePath path = enumerator.Next(); !path.empty();
+       path = enumerator.Next()) {
+    LOG(INFO) << "Loading trait definition from " << path.value();
+    std::string json;
+    CHECK(LoadFile(path, &json, nullptr));
+    device->AddTraitDefinitionsFromJson(json);
+  }
+}
+
 void LoadCommandDefinitions(const BuffetConfig::Options& options,
                             weave::Device* device) {
   auto load_packages = [device](const base::FilePath& root,
@@ -97,7 +114,8 @@
     }
   };
   load_packages(options.definitions, FILE_PATH_LITERAL("*.json"));
-  load_packages(options.test_definitions, FILE_PATH_LITERAL("*test.json"));
+  if (!options.test_definitions.empty())
+    load_packages(options.test_definitions, FILE_PATH_LITERAL("*test.json"));
 }
 
 void LoadStateDefinitions(const BuffetConfig::Options& options,
@@ -207,6 +225,7 @@
                                   mdns_client_.get(), web_serv_client_.get(),
                                   shill_client_.get(), bluetooth_client_.get());
 
+  LoadTraitDefinitions(options_.config_options, device_.get());
   LoadCommandDefinitions(options_.config_options, device_.get());
   LoadStateDefinitions(options_.config_options, device_.get());
   LoadStateDefaults(options_.config_options, device_.get());
@@ -267,7 +286,20 @@
   response->Return(device_->GetSettings().cloud_id);
 }
 
+void Manager::AddComponent(DBusMethodResponsePtr<> response,
+                           const std::string& name,
+                           const std::vector<std::string>& traits) {
+  brillo::ErrorPtr brillo_error;
+  weave::ErrorPtr error;
+  if (!device_->AddComponent(name, traits, &error)) {
+    ConvertError(*error, &brillo_error);
+    return response->ReplyWithError(brillo_error.get());
+  }
+  response->Return();
+}
+
 void Manager::UpdateState(DBusMethodResponsePtr<> response,
+                          const std::string& component,
                           const brillo::VariantDictionary& property_set) {
   brillo::ErrorPtr brillo_error;
   auto properties =
@@ -276,7 +308,7 @@
     return response->ReplyWithError(brillo_error.get());
 
   weave::ErrorPtr error;
-  if (!device_->SetStateProperties(*properties, &error)) {
+  if (!device_->SetStateProperties(component, *properties, &error)) {
     ConvertError(*error, &brillo_error);
     return response->ReplyWithError(brillo_error.get());
   }
diff --git a/buffet/manager.h b/buffet/manager.h
index e8ddcc1..1dea711 100644
--- a/buffet/manager.h
+++ b/buffet/manager.h
@@ -87,7 +87,11 @@
   // DBus methods:
   void RegisterDevice(DBusMethodResponsePtr<std::string> response,
                       const std::string& ticket_id) override;
+  void AddComponent(DBusMethodResponsePtr<> response,
+                    const std::string& name,
+                    const std::vector<std::string>& traits) override;
   void UpdateState(DBusMethodResponsePtr<> response,
+                   const std::string& component,
                    const brillo::VariantDictionary& property_set) override;
   bool GetState(brillo::ErrorPtr* error, std::string* state) override;
   void AddCommand(DBusMethodResponsePtr<std::string> response,
diff --git a/libweaved/command.cc b/libweaved/command.cc
index 8b7427f..caee8b0 100644
--- a/libweaved/command.cc
+++ b/libweaved/command.cc
@@ -31,6 +31,10 @@
   return proxy_->name();
 }
 
+const std::string& Command::GetComponent() const {
+  return proxy_->component();
+}
+
 Command::State Command::GetState() const {
   std::string state = proxy_->state();
   if (state == "queued")
diff --git a/libweaved/command.h b/libweaved/command.h
index f68b150..a8332e5 100644
--- a/libweaved/command.h
+++ b/libweaved/command.h
@@ -37,6 +37,30 @@
 
 class Device;
 
+namespace detail {
+
+// Helper function for Command::GetParameter<T>. Allows specialization.
+template <typename T>
+inline bool GetValue(const brillo::Any& any, T* value) {
+  return any.GetValue<T>(value);
+}
+
+// Specialization for double, allow to extract a double from an int.
+template <>
+inline bool GetValue<double>(const brillo::Any& any, double* value) {
+  if (any.GetValue<double>(value))
+    return true;
+
+  int int_val = 0;
+  if (!any.GetValue<int>(&int_val))
+    return false;
+
+  *value = static_cast<double>(int_val);
+  return true;
+}
+
+}  // namespace detail
+
 class LIBWEAVED_EXPORT Command final {
  public:
   enum class State {
@@ -58,6 +82,9 @@
   // Returns the full name of the command.
   const std::string& GetName() const;
 
+  // Returns the name of the component this command was sent to.
+  const std::string& GetComponent() const;
+
   // Returns the command state.
   Command::State GetState() const;
 
@@ -77,7 +104,7 @@
     T value{};
     auto p = parameters.find(name);
     if (p != parameters.end())
-      p->second.GetValue<T>(&value);
+      detail::GetValue<T>(p->second, &value);
     return value;
   }
 
diff --git a/libweaved/device.cc b/libweaved/device.cc
index 0da4763..5f396a7 100644
--- a/libweaved/device.cc
+++ b/libweaved/device.cc
@@ -46,13 +46,39 @@
   return std::unique_ptr<Device>{new Device{bus, state_required_callback}};
 }
 
-void Device::AddCommandHandler(const std::string& command_name,
+void Device::AddComponent(const std::string& component,
+                          const std::vector<std::string>& traits) {
+  ComponentEntry entry;
+  entry.component = component;
+  entry.traits = traits;
+  components_.push_back(std::move(entry));
+  if (proxy_)
+    proxy_->AddComponent(component, traits, nullptr);
+}
+
+void Device::AddCommandHandler(const std::string& component,
+                               const std::string& command_name,
                                const CommandHandlerCallback& callback) {
-  command_handler_map_.emplace(command_name, callback);
+  for (const auto& entry : command_handlers_) {
+    if (entry.command_name != command_name)
+      continue;
+    // The command names are the same, make sure we have different components.
+    // This means that both component names are not empty and are different.
+    CHECK(!component.empty() && !entry.component.empty() &&
+          component != entry.component)
+        << "Handler for " << component << ":" << command_name << " already set";
+  }
+  CommandHandlerEntry entry;
+  entry.component = component;
+  entry.command_name = command_name;
+  entry.callback = callback;
+
+  command_handlers_.push_back(std::move(entry));
 
   // If there are any commands already received, call the handler immediately.
   for (auto& pair : command_map_) {
-    if (pair.first->name() == command_name) {
+    if (pair.first->name() == command_name &&
+        (component.empty() || pair.first->component() == component)) {
       if (!pair.second)
         pair.second.reset(new Command{pair.first});
       callback.Run(pair.second);
@@ -60,30 +86,49 @@
   }
 }
 
-bool Device::SetStateProperties(const brillo::VariantDictionary& dict,
+bool Device::SetStateProperties(const std::string& component,
+                                const brillo::VariantDictionary& dict,
                                 brillo::ErrorPtr* error) {
   if (proxy_)
-    return proxy_->UpdateState(dict, error);
+    return proxy_->UpdateState(component, dict, error);
 
   brillo::Error::AddTo(error, FROM_HERE, "weaved", "service_unavailable",
-                         "Process 'weaved' is unreachable");
+                       "Process 'weaved' is unreachable");
   return false;
 }
 
+bool Device::SetStateProperty(const std::string& component,
+                              const std::string& name,
+                              const brillo::Any& value,
+                              brillo::ErrorPtr* error) {
+  return SetStateProperties(component, brillo::VariantDictionary{{name, value}},
+                            error);
+}
+
+void Device::AddCommandHandler(const std::string& command_name,
+                               const CommandHandlerCallback& callback) {
+  AddCommandHandler("", command_name, callback);
+}
+
+bool Device::SetStateProperties(const brillo::VariantDictionary& dict,
+                                brillo::ErrorPtr* error) {
+  return SetStateProperties("", dict, error);
+}
+
 bool Device::SetStateProperty(const std::string& name,
                               const brillo::Any& value,
                               brillo::ErrorPtr* error) {
-  return SetStateProperties(brillo::VariantDictionary{{name, value}}, error);
+  return SetStateProperty("", name, value, error);
 }
 
 void Device::OnCommandAdded(CommandProxyInterface* proxy) {
   std::shared_ptr<Command>& command = command_map_[proxy];
-  auto iter = command_handler_map_.find(proxy->name());
-  if (iter == command_handler_map_.end())
+  const Device::CommandHandlerCallback* callback = FindHandlerForCommand(proxy);
+  if (!callback)
     return;
   if (!command)
     command.reset(new Command{proxy});
-  iter->second.Run(command);
+  callback->Run(command);
 }
 
 void Device::OnCommandRemoved(const dbus::ObjectPath& object_path) {
@@ -95,6 +140,8 @@
 
 void Device::OnManagerAdded(ManagerProxyInterface* proxy) {
   proxy_ = proxy;
+  for (const auto& entry : components_)
+    proxy_->AddComponent(entry.component, entry.traits, nullptr);
   state_required_callback_.Run();
 }
 
@@ -102,4 +149,16 @@
   proxy_ = nullptr;
 }
 
+const Device::CommandHandlerCallback* Device::FindHandlerForCommand(
+    com::android::Weave::CommandProxyInterface* proxy) const {
+  for (const auto& entry : command_handlers_) {
+    if (proxy->name() == entry.command_name &&
+        (entry.component.empty() || proxy->component() == entry.component)) {
+      return &entry.callback;
+    }
+  }
+  return nullptr;
+}
+
+
 }  // namespace weave
diff --git a/libweaved/device.h b/libweaved/device.h
index c1c5dd5..376b656 100644
--- a/libweaved/device.h
+++ b/libweaved/device.h
@@ -21,6 +21,7 @@
 #include <map>
 #include <memory>
 #include <string>
+#include <vector>
 
 #include <base/callback.h>
 #include <base/macros.h>
@@ -60,23 +61,44 @@
   using CommandHandlerCallback =
       base::Callback<void(const std::weak_ptr<Command>& command)>;
 
+  void AddComponent(const std::string& component,
+                    const std::vector<std::string>& traits);
+
   // Sets handler for new commands added to the queue.
   // |command_name| is the full command name of the command to handle. e.g.
   // "base.reboot". Each command can have no more than one handler.
-  // Empty |command_name| sets default handler for all unhanded commands.
-  // No new command handlers can be set after default handler was set.
-  void AddCommandHandler(const std::string& command_name,
+  void AddCommandHandler(const std::string& component,
+                         const std::string& command_name,
                          const CommandHandlerCallback& callback);
 
-  bool SetStateProperties(const brillo::VariantDictionary& dict,
+  bool SetStateProperties(const std::string& component,
+                          const brillo::VariantDictionary& dict,
                           brillo::ErrorPtr* error);
 
   // Sets value of the single property.
   // |name| is full property name, including package name. e.g. "base.network".
-  bool SetStateProperty(const std::string& name,
+  bool SetStateProperty(const std::string& component,
+                        const std::string& name,
                         const brillo::Any& value,
                         brillo::ErrorPtr* error);
 
+  // Sets handler for new commands added to the queue.
+  // |command_name| is the full command name of the command to handle. e.g.
+  // "base.reboot". Each command can have no more than one handler.
+  LIBWEAVED_DEPRECATED void AddCommandHandler(
+      const std::string& command_name,
+      const CommandHandlerCallback& callback);
+
+  LIBWEAVED_DEPRECATED bool SetStateProperties(
+      const brillo::VariantDictionary& dict,
+      brillo::ErrorPtr* error);
+
+  // Sets value of the single property.
+  // |name| is full property name, including package name. e.g. "base.network".
+  LIBWEAVED_DEPRECATED bool SetStateProperty(const std::string& name,
+                                             const brillo::Any& value,
+                                             brillo::ErrorPtr* error);
+
  private:
   Device(const scoped_refptr<dbus::Bus>& bus,
          const base::Closure& state_required_callback);
@@ -87,13 +109,29 @@
   void OnManagerAdded(com::android::Weave::ManagerProxyInterface* proxy);
   void OnManagerRemoved(const dbus::ObjectPath& object_path);
 
+  const CommandHandlerCallback* FindHandlerForCommand(
+      com::android::Weave::CommandProxyInterface* proxy) const;
+
   std::unique_ptr<com::android::Weave::ObjectManagerProxy> weaved_object_mgr_;
   com::android::Weave::ManagerProxyInterface* proxy_{nullptr};
 
   using CommandMap = std::map<com::android::Weave::CommandProxyInterface*,
                               std::shared_ptr<Command>>;
   CommandMap command_map_;
-  std::map<std::string, CommandHandlerCallback> command_handler_map_;
+
+  struct CommandHandlerEntry {
+    std::string component;
+    std::string command_name;
+    CommandHandlerCallback callback;
+  };
+  std::vector<CommandHandlerEntry> command_handlers_;
+
+  struct ComponentEntry {
+    std::string component;
+    std::vector<std::string> traits;
+  };
+  std::vector<ComponentEntry> components_;
+
   scoped_refptr<dbus::Bus> bus_;
   base::Closure state_required_callback_;
 
diff --git a/libweaved/export.h b/libweaved/export.h
index eb88023..4b7999f 100644
--- a/libweaved/export.h
+++ b/libweaved/export.h
@@ -18,4 +18,6 @@
 #define LIBWEAVED_EXPORT __attribute__((__visibility__("default")))
 #define LIBWEAVED_PRIVATE __attribute__((__visibility__("hidden")))
 
+#define LIBWEAVED_DEPRECATED __attribute__((deprecated))
+
 #endif  // LIBWEAVED_EXPORT_H_