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