buffet: Move privetd sources into buffet

No functional changes, only renaming, fixed include paths and include
guards to avoid resubmit warnings.

BUG=brillo:1161
CQ-DEPEND=CL:276521
TEST=none

Change-Id: Icacff92aef47fdd46542bc96eba3ffbb4df6241a
Reviewed-on: https://chromium-review.googlesource.com/276319
Reviewed-by: Vitaly Buka <vitalybuka@chromium.org>
Commit-Queue: Vitaly Buka <vitalybuka@chromium.org>
Tested-by: Vitaly Buka <vitalybuka@chromium.org>
diff --git a/buffet/privet/cloud_delegate.cc b/buffet/privet/cloud_delegate.cc
new file mode 100644
index 0000000..5de432e
--- /dev/null
+++ b/buffet/privet/cloud_delegate.cc
@@ -0,0 +1,532 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "buffet/privet/cloud_delegate.h"
+
+#include <map>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/json/json_reader.h>
+#include <base/json/json_writer.h>
+#include <base/logging.h>
+#include <base/memory/weak_ptr.h>
+#include <base/message_loop/message_loop.h>
+#include <base/values.h>
+#include <chromeos/errors/error.h>
+#include <chromeos/variant_dictionary.h>
+#include <dbus/bus.h>
+
+#include "buffet/dbus-proxies.h"
+#include "buffet/privet/constants.h"
+
+namespace privetd {
+
+namespace {
+
+using chromeos::ErrorPtr;
+using chromeos::VariantDictionary;
+using org::chromium::Buffet::ManagerProxy;
+using org::chromium::Buffet::ObjectManagerProxy;
+
+const int kMaxSetupRetries = 5;
+const int kFirstRetryTimeoutSec = 1;
+
+class CloudDelegateImpl : public CloudDelegate {
+ public:
+  CloudDelegateImpl(const scoped_refptr<dbus::Bus>& bus,
+                    bool is_gcd_setup_enabled)
+      : object_manager_{bus}, is_gcd_setup_enabled_(is_gcd_setup_enabled) {
+    object_manager_.SetManagerAddedCallback(
+        base::Bind(&CloudDelegateImpl::OnManagerAdded,
+                   weak_factory_.GetWeakPtr()));
+    object_manager_.SetManagerRemovedCallback(
+        base::Bind(&CloudDelegateImpl::OnManagerRemoved,
+                   weak_factory_.GetWeakPtr()));
+    object_manager_.SetCommandRemovedCallback(base::Bind(
+        &CloudDelegateImpl::OnCommandRemoved, weak_factory_.GetWeakPtr()));
+  }
+
+  ~CloudDelegateImpl() override = default;
+
+  bool GetModelId(std::string* id, chromeos::ErrorPtr* error) const override {
+    if (!IsManagerReady(error))
+      return false;
+    if (manager_->model_id().size() != 5) {
+      chromeos::Error::AddToPrintf(
+          error, FROM_HERE, errors::kDomain, errors::kInvalidState,
+          "Model ID is invalid: %s", manager_->model_id().c_str());
+      return false;
+    }
+    *id = manager_->model_id();
+    return true;
+  }
+
+  bool GetName(std::string* name, chromeos::ErrorPtr* error) const override {
+    if (!IsManagerReady(error))
+      return false;
+    *name = manager_->name();
+    return true;
+  }
+
+  std::string GetDescription() const override {
+    return manager_ ? manager_->description() : std::string{};
+  }
+
+  std::string GetLocation() const override {
+    return manager_ ? manager_->location() : std::string{};
+  }
+
+  void UpdateDeviceInfo(const std::string& name,
+                        const std::string& description,
+                        const std::string& location,
+                        const base::Closure& success_callback,
+                        const ErrorCallback& error_callback) override {
+    chromeos::ErrorPtr error;
+    if (!IsManagerReady(&error))
+      return error_callback.Run(error.get());
+
+    if (name == manager_->name() && description == manager_->description() &&
+        location == manager_->location()) {
+      return success_callback.Run();
+    }
+
+    manager_->UpdateDeviceInfoAsync(name, description, location,
+                                    success_callback, error_callback);
+  }
+
+  std::string GetOemName() const override {
+    return manager_ ? manager_->oem_name() : std::string{};
+  }
+
+  std::string GetModelName() const override {
+    return manager_ ? manager_->model_name() : std::string{};
+  }
+
+  std::set<std::string> GetServices() const override {
+    std::set<std::string> result;
+    for (base::DictionaryValue::Iterator it{command_defs_}; !it.IsAtEnd();
+         it.Advance()) {
+      result.emplace(it.key());
+    }
+    return result;
+  }
+
+  AuthScope GetAnonymousMaxScope() const override {
+    if (manager_) {
+      AuthScope scope;
+      if (StringToAuthScope(manager_->anonymous_access_role(), &scope))
+        return scope;
+    }
+    return AuthScope::kNone;
+  }
+
+  const ConnectionState& GetConnectionState() const override {
+    return connection_state_;
+  }
+
+  const SetupState& GetSetupState() const override { return setup_state_; }
+
+  bool Setup(const std::string& ticket_id,
+             const std::string& user,
+             chromeos::ErrorPtr* error) override {
+    if (!is_gcd_setup_enabled_) {
+      chromeos::Error::AddTo(error, FROM_HERE, errors::kDomain,
+                             errors::kSetupUnavailable,
+                             "GCD setup unavailible");
+      return false;
+    }
+    if (!object_manager_.GetManagerProxy()) {
+      chromeos::Error::AddTo(error, FROM_HERE, errors::kDomain,
+                             errors::kDeviceBusy, "Buffet is not ready");
+      return false;
+    }
+    if (setup_state_.IsStatusEqual(SetupState::kInProgress)) {
+      chromeos::Error::AddTo(error, FROM_HERE, errors::kDomain,
+                             errors::kDeviceBusy, "Setup in progress");
+      return false;
+    }
+    VLOG(1) << "GCD Setup started. ticket_id: " << ticket_id
+            << ", user:" << user;
+    setup_state_ = SetupState(SetupState::kInProgress);
+    setup_weak_factory_.InvalidateWeakPtrs();
+    base::MessageLoop::current()->PostDelayedTask(
+        FROM_HERE, base::Bind(&CloudDelegateImpl::CallManagerRegisterDevice,
+                              setup_weak_factory_.GetWeakPtr(), ticket_id, 0),
+        base::TimeDelta::FromSeconds(kSetupDelaySeconds));
+    // Return true because we tried setup.
+    return true;
+  }
+
+  std::string GetCloudId() const override {
+    return manager_ ? manager_->device_id() : std::string{};
+  }
+
+  const base::DictionaryValue& GetState() const override { return state_; }
+
+  const base::DictionaryValue& GetCommandDef() const override {
+    return command_defs_;
+  }
+
+  void AddCommand(const base::DictionaryValue& command,
+                  const UserInfo& user_info,
+                  const SuccessCallback& success_callback,
+                  const ErrorCallback& error_callback) override {
+    CHECK(user_info.scope() != AuthScope::kNone);
+
+    chromeos::ErrorPtr error;
+    if (!IsManagerReady(&error))
+      return error_callback.Run(error.get());
+
+    std::string command_str;
+    base::JSONWriter::Write(&command, &command_str);
+    manager_->AddCommandAsync(
+        command_str, AuthScopeToString(user_info.scope()),
+        base::Bind(&CloudDelegateImpl::OnAddCommandSucceeded,
+                   weak_factory_.GetWeakPtr(), success_callback,
+                   error_callback),
+        error_callback);
+  }
+
+  void GetCommand(const std::string& id,
+                  const UserInfo& user_info,
+                  const SuccessCallback& success_callback,
+                  const ErrorCallback& error_callback) override {
+    CHECK(user_info.scope() != AuthScope::kNone);
+    chromeos::ErrorPtr error;
+    if (!CanAccessCommand(id, user_info)) {
+      chromeos::Error::AddTo(&error, FROM_HERE, errors::kDomain,
+                             errors::kAccessDenied,
+                             "Need to be owner of the command.");
+      return error_callback.Run(error.get());
+    }
+
+    GetCommandInternal(id, success_callback, error_callback);
+  }
+
+  void CancelCommand(const std::string& id,
+                     const UserInfo& user_info,
+                     const SuccessCallback& success_callback,
+                     const ErrorCallback& error_callback) override {
+    CHECK(user_info.scope() != AuthScope::kNone);
+    chromeos::ErrorPtr error;
+    if (!CanAccessCommand(id, user_info)) {
+      chromeos::Error::AddTo(&error, FROM_HERE, errors::kDomain,
+                             errors::kAccessDenied,
+                             "Need to be owner of the command.");
+      return error_callback.Run(error.get());
+    }
+
+    for (auto command : object_manager_.GetCommandInstances()) {
+      if (command->id() == id) {
+        return command->CancelAsync(
+            base::Bind(&CloudDelegateImpl::GetCommandInternal,
+                       weak_factory_.GetWeakPtr(), id, success_callback,
+                       error_callback),
+            error_callback);
+      }
+    }
+
+    chromeos::Error::AddToPrintf(&error, FROM_HERE, errors::kDomain,
+                                 errors::kNotFound,
+                                 "Command not found, ID='%s'", id.c_str());
+    error_callback.Run(error.get());
+  }
+
+  void ListCommands(const UserInfo& user_info,
+                    const SuccessCallback& success_callback,
+                    const ErrorCallback& error_callback) override {
+    CHECK(user_info.scope() != AuthScope::kNone);
+
+    std::vector<org::chromium::Buffet::CommandProxy*> commands{
+        object_manager_.GetCommandInstances()};
+
+    auto ids = std::make_shared<std::vector<std::string>>();
+    for (auto command : commands) {
+      if (CanAccessCommand(command->id(), user_info))
+        ids->push_back(command->id());
+    }
+
+    GetNextCommand(ids, std::make_shared<base::ListValue>(), success_callback,
+                   error_callback, base::DictionaryValue{});
+  }
+
+ private:
+  void GetCommandInternal(const std::string& id,
+                          const SuccessCallback& success_callback,
+                          const ErrorCallback& error_callback) {
+    chromeos::ErrorPtr error;
+    if (!IsManagerReady(&error))
+      return error_callback.Run(error.get());
+    manager_->GetCommandAsync(
+        id, base::Bind(&CloudDelegateImpl::OnGetCommandSucceeded,
+                       weak_factory_.GetWeakPtr(), success_callback,
+                       error_callback),
+        error_callback);
+  }
+
+  void OnManagerAdded(ManagerProxy* manager) {
+    manager_ = manager;
+    manager_->SetPropertyChangedCallback(
+        base::Bind(&CloudDelegateImpl::OnManagerPropertyChanged,
+                   weak_factory_.GetWeakPtr()));
+    // Read all initial values.
+    OnManagerPropertyChanged(manager, std::string{});
+  }
+
+  void OnCommandRemoved(const dbus::ObjectPath& object_path) {
+    command_owners_.erase(object_manager_.GetCommandProxy(object_path)->id());
+  }
+
+  void OnManagerPropertyChanged(ManagerProxy* manager,
+                                const std::string& property_name) {
+    CHECK_EQ(manager_, manager);
+
+    if (property_name.empty() || property_name == ManagerProxy::StatusName()) {
+      OnStatusPropertyChanged();
+    }
+
+    if (property_name.empty() ||
+        property_name == ManagerProxy::DeviceIdName() ||
+        property_name == ManagerProxy::OemNameName() ||
+        property_name == ManagerProxy::ModelNameName() ||
+        property_name == ManagerProxy::ModelIdName() ||
+        property_name == ManagerProxy::NameName() ||
+        property_name == ManagerProxy::DescriptionName() ||
+        property_name == ManagerProxy::LocationName() ||
+        property_name == ManagerProxy::AnonymousAccessRoleName()) {
+      NotifyOnDeviceInfoChanged();
+    }
+
+    if (property_name.empty() || property_name == ManagerProxy::StateName()) {
+      OnStatePropertyChanged();
+    }
+
+    if (property_name.empty() ||
+        property_name == ManagerProxy::CommandDefsName()) {
+      OnCommandDefsPropertyChanged();
+    }
+  }
+
+  void OnStatusPropertyChanged() {
+    const std::string& status = manager_->status();
+    if (status == "unconfigured") {
+      connection_state_ = ConnectionState{ConnectionState::kUnconfigured};
+    } else if (status == "connecting") {
+      // TODO(vitalybuka): Find conditions for kOffline.
+      connection_state_ = ConnectionState{ConnectionState::kConnecting};
+    } else if (status == "connected") {
+      connection_state_ = ConnectionState{ConnectionState::kOnline};
+    } else {
+      chromeos::ErrorPtr error;
+      chromeos::Error::AddToPrintf(
+          &error, FROM_HERE, errors::kDomain, errors::kInvalidState,
+          "Unexpected buffet status: %s", status.c_str());
+      connection_state_ = ConnectionState{std::move(error)};
+    }
+    NotifyOnDeviceInfoChanged();
+  }
+
+  void OnStatePropertyChanged() {
+    state_.Clear();
+    std::unique_ptr<base::Value> value{
+        base::JSONReader::Read(manager_->state())};
+    const base::DictionaryValue* state{nullptr};
+    if (value && value->GetAsDictionary(&state))
+      state_.MergeDictionary(state);
+    NotifyOnStateChanged();
+  }
+
+  void OnCommandDefsPropertyChanged() {
+    command_defs_.Clear();
+    std::unique_ptr<base::Value> value{
+        base::JSONReader::Read(manager_->command_defs())};
+    const base::DictionaryValue* defs{nullptr};
+    if (value && value->GetAsDictionary(&defs))
+      command_defs_.MergeDictionary(defs);
+    NotifyOnCommandDefsChanged();
+  }
+
+  void OnManagerRemoved(const dbus::ObjectPath& path) {
+    manager_ = nullptr;
+    connection_state_ = ConnectionState(ConnectionState::kDisabled);
+    state_.Clear();
+    command_defs_.Clear();
+    command_owners_.clear();
+    NotifyOnDeviceInfoChanged();
+    NotifyOnCommandDefsChanged();
+    NotifyOnStateChanged();
+  }
+
+  void RetryRegister(const std::string& ticket_id,
+                     int retries,
+                     chromeos::Error* error) {
+    if (retries >= kMaxSetupRetries) {
+      chromeos::ErrorPtr new_error{error ? error->Clone() : nullptr};
+      chromeos::Error::AddTo(&new_error, FROM_HERE, errors::kDomain,
+                             errors::kInvalidState,
+                             "Failed to register device");
+      setup_state_ = SetupState{std::move(new_error)};
+      return;
+    }
+    base::MessageLoop::current()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&CloudDelegateImpl::CallManagerRegisterDevice,
+                   setup_weak_factory_.GetWeakPtr(), ticket_id, retries + 1),
+        base::TimeDelta::FromSeconds(kFirstRetryTimeoutSec << retries));
+  }
+
+  void OnRegisterSuccess(const std::string& device_id) {
+    VLOG(1) << "Device registered: " << device_id;
+    setup_state_ = SetupState(SetupState::kSuccess);
+  }
+
+  void CallManagerRegisterDevice(const std::string& ticket_id, int retries) {
+    auto manager_proxy = object_manager_.GetManagerProxy();
+    if (!manager_proxy) {
+      LOG(ERROR) << "Couldn't register because Buffet was offline.";
+      RetryRegister(ticket_id, retries, nullptr);
+      return;
+    }
+    manager_proxy->RegisterDeviceAsync(
+        ticket_id, base::Bind(&CloudDelegateImpl::OnRegisterSuccess,
+                              setup_weak_factory_.GetWeakPtr()),
+        base::Bind(&CloudDelegateImpl::RetryRegister,
+                   setup_weak_factory_.GetWeakPtr(), ticket_id, retries));
+  }
+
+  void OnAddCommandSucceeded(const SuccessCallback& success_callback,
+                             const ErrorCallback& error_callback,
+                             const std::string& id) {
+    GetCommandInternal(id, success_callback, error_callback);
+  }
+
+  void OnGetCommandSucceeded(const SuccessCallback& success_callback,
+                             const ErrorCallback& error_callback,
+                             const std::string& json_command) {
+    std::unique_ptr<base::Value> value{base::JSONReader::Read(json_command)};
+    base::DictionaryValue* command{nullptr};
+    if (!value || !value->GetAsDictionary(&command)) {
+      chromeos::ErrorPtr error;
+      chromeos::Error::AddTo(&error, FROM_HERE, errors::kDomain,
+                             errors::kInvalidFormat,
+                             "Buffet returned invalid JSON");
+      return error_callback.Run(error.get());
+    }
+    success_callback.Run(*command);
+  }
+
+  void GetNextCommandSkipError(
+      const std::shared_ptr<std::vector<std::string>>& ids,
+      const std::shared_ptr<base::ListValue>& commands,
+      const SuccessCallback& success_callback,
+      const ErrorCallback& error_callback,
+      chromeos::Error*) {
+    // Ignore if we can't get some commands. Maybe they were removed.
+    GetNextCommand(ids, commands, success_callback, error_callback,
+                   base::DictionaryValue{});
+  }
+
+  void GetNextCommand(const std::shared_ptr<std::vector<std::string>>& ids,
+                      const std::shared_ptr<base::ListValue>& commands,
+                      const SuccessCallback& success_callback,
+                      const ErrorCallback& error_callback,
+                      const base::DictionaryValue& json) {
+    if (!json.empty())
+      commands->Append(json.DeepCopy());
+
+    if (ids->empty()) {
+      base::DictionaryValue commands_json;
+      commands_json.Set("commands", commands->DeepCopy());
+      return success_callback.Run(commands_json);
+    }
+
+    std::string next_id = ids->back();
+    ids->pop_back();
+
+    auto on_success = base::Bind(&CloudDelegateImpl::GetNextCommand,
+                                 weak_factory_.GetWeakPtr(), ids, commands,
+                                 success_callback, error_callback);
+
+    auto on_error = base::Bind(&CloudDelegateImpl::GetNextCommandSkipError,
+                               weak_factory_.GetWeakPtr(), ids, commands,
+                               success_callback, error_callback);
+
+    GetCommandInternal(next_id, on_success, on_error);
+  }
+
+  bool IsManagerReady(chromeos::ErrorPtr* error) const {
+    if (!manager_) {
+      chromeos::Error::AddTo(error, FROM_HERE, errors::kDomain,
+                             errors::kDeviceBusy, "Buffet is not ready");
+      return false;
+    }
+    return true;
+  }
+
+  bool CanAccessCommand(const std::string& command_id,
+                        const UserInfo& user_info) const {
+    if (user_info.scope() == AuthScope::kOwner)
+      return true;
+    auto it = command_owners_.find(command_id);
+    return it != command_owners_.end() && it->second == user_info.user_id();
+  }
+
+  ObjectManagerProxy object_manager_;
+
+  bool is_gcd_setup_enabled_{false};
+
+  ManagerProxy* manager_{nullptr};
+
+  // Primary state of GCD.
+  ConnectionState connection_state_{ConnectionState::kDisabled};
+
+  // State of the current or last setup.
+  SetupState setup_state_{SetupState::kNone};
+
+  // Current device state.
+  base::DictionaryValue state_;
+
+  // Current commands definitions.
+  base::DictionaryValue command_defs_;
+
+  // Map of command IDs to user IDs.
+  std::map<std::string, uint64_t> command_owners_;
+
+  // |setup_weak_factory_| tracks the lifetime of callbacks used in connection
+  // with a particular invocation of Setup().
+  base::WeakPtrFactory<CloudDelegateImpl> setup_weak_factory_{this};
+  // |weak_factory_| tracks the lifetime of |this|.
+  base::WeakPtrFactory<CloudDelegateImpl> weak_factory_{this};
+};
+
+}  // namespace
+
+CloudDelegate::CloudDelegate() {
+}
+
+CloudDelegate::~CloudDelegate() {
+}
+
+// static
+std::unique_ptr<CloudDelegate> CloudDelegate::CreateDefault(
+    const scoped_refptr<dbus::Bus>& bus,
+    bool is_gcd_setup_enabled) {
+  return std::unique_ptr<CloudDelegateImpl>{
+      new CloudDelegateImpl{bus, is_gcd_setup_enabled}};
+}
+
+void CloudDelegate::NotifyOnDeviceInfoChanged() {
+  FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceInfoChanged());
+}
+
+void CloudDelegate::NotifyOnCommandDefsChanged() {
+  FOR_EACH_OBSERVER(Observer, observer_list_, OnCommandDefsChanged());
+}
+
+void CloudDelegate::NotifyOnStateChanged() {
+  FOR_EACH_OBSERVER(Observer, observer_list_, OnStateChanged());
+}
+
+}  // namespace privetd