• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 The Weave Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/privet/cloud_delegate.h"
6 
7 #include <map>
8 #include <vector>
9 
10 #include <base/bind.h>
11 #include <base/logging.h>
12 #include <base/memory/weak_ptr.h>
13 #include <base/values.h>
14 #include <weave/error.h>
15 #include <weave/provider/task_runner.h>
16 
17 #include "src/backoff_entry.h"
18 #include "src/component_manager.h"
19 #include "src/config.h"
20 #include "src/device_registration_info.h"
21 #include "src/privet/constants.h"
22 
23 namespace weave {
24 namespace privet {
25 
26 namespace {
27 
28 const BackoffEntry::Policy register_backoff_policy = {0,    1000, 2.0,  0.2,
29                                                       5000, -1,   false};
30 
31 const int kMaxDeviceRegistrationRetries = 100;  // ~ 8 minutes @5s retries.
32 
ReturnNotFound(const std::string & command_id,ErrorPtr * error)33 CommandInstance* ReturnNotFound(const std::string& command_id,
34                                 ErrorPtr* error) {
35   Error::AddToPrintf(error, FROM_HERE, errors::kNotFound,
36                      "Command not found, ID='%s'", command_id.c_str());
37   return nullptr;
38 }
39 
40 class CloudDelegateImpl : public CloudDelegate {
41  public:
CloudDelegateImpl(provider::TaskRunner * task_runner,DeviceRegistrationInfo * device,ComponentManager * component_manager)42   CloudDelegateImpl(provider::TaskRunner* task_runner,
43                     DeviceRegistrationInfo* device,
44                     ComponentManager* component_manager)
45       : task_runner_{task_runner},
46         device_{device},
47         component_manager_{component_manager} {
48     device_->GetMutableConfig()->AddOnChangedCallback(base::Bind(
49         &CloudDelegateImpl::OnConfigChanged, weak_factory_.GetWeakPtr()));
50     device_->AddGcdStateChangedCallback(base::Bind(
51         &CloudDelegateImpl::OnRegistrationChanged, weak_factory_.GetWeakPtr()));
52 
53     component_manager_->AddTraitDefChangedCallback(
54         base::Bind(&CloudDelegateImpl::NotifyOnTraitDefsChanged,
55                    weak_factory_.GetWeakPtr()));
56     component_manager_->AddCommandAddedCallback(base::Bind(
57         &CloudDelegateImpl::OnCommandAdded, weak_factory_.GetWeakPtr()));
58     component_manager_->AddCommandRemovedCallback(base::Bind(
59         &CloudDelegateImpl::OnCommandRemoved, weak_factory_.GetWeakPtr()));
60     component_manager_->AddStateChangedCallback(base::Bind(
61         &CloudDelegateImpl::NotifyOnStateChanged, weak_factory_.GetWeakPtr()));
62     component_manager_->AddComponentTreeChangedCallback(
63         base::Bind(&CloudDelegateImpl::NotifyOnComponentTreeChanged,
64                    weak_factory_.GetWeakPtr()));
65   }
66 
67   ~CloudDelegateImpl() override = default;
68 
GetDeviceId() const69   std::string GetDeviceId() const override {
70     return device_->GetSettings().device_id;
71   }
72 
GetModelId() const73   std::string GetModelId() const override {
74     CHECK_EQ(5u, device_->GetSettings().model_id.size());
75     return device_->GetSettings().model_id;
76   }
77 
GetName() const78   std::string GetName() const override { return device_->GetSettings().name; }
79 
GetDescription() const80   std::string GetDescription() const override {
81     return device_->GetSettings().description;
82   }
83 
GetLocation() const84   std::string GetLocation() const override {
85     return device_->GetSettings().location;
86   }
87 
UpdateDeviceInfo(const std::string & name,const std::string & description,const std::string & location)88   void UpdateDeviceInfo(const std::string& name,
89                         const std::string& description,
90                         const std::string& location) override {
91     device_->UpdateDeviceInfo(name, description, location);
92   }
93 
GetOemName() const94   std::string GetOemName() const override {
95     return device_->GetSettings().oem_name;
96   }
97 
GetModelName() const98   std::string GetModelName() const override {
99     return device_->GetSettings().model_name;
100   }
101 
GetAnonymousMaxScope() const102   AuthScope GetAnonymousMaxScope() const override {
103     return device_->GetSettings().local_anonymous_access_role;
104   }
105 
GetConnectionState() const106   const ConnectionState& GetConnectionState() const override {
107     return connection_state_;
108   }
109 
GetSetupState() const110   const SetupState& GetSetupState() const override { return setup_state_; }
111 
Setup(const std::string & ticket_id,const std::string & user,ErrorPtr * error)112   bool Setup(const std::string& ticket_id,
113              const std::string& user,
114              ErrorPtr* error) override {
115     VLOG(1) << "GCD Setup started. ticket_id: " << ticket_id
116             << ", user:" << user;
117     // Set (or reset) the retry counter, since we are starting a new
118     // registration process.
119     registation_retry_count_ = kMaxDeviceRegistrationRetries;
120     ticket_id_ = ticket_id;
121     if (setup_state_.IsStatusEqual(SetupState::kInProgress)) {
122       // Another registration is in progress. In case it fails, we will use
123       // the new ticket ID when retrying the request.
124       return true;
125     }
126     setup_state_ = SetupState(SetupState::kInProgress);
127     setup_weak_factory_.InvalidateWeakPtrs();
128     backoff_entry_.Reset();
129     task_runner_->PostDelayedTask(
130         FROM_HERE, base::Bind(&CloudDelegateImpl::CallManagerRegisterDevice,
131                               setup_weak_factory_.GetWeakPtr()),
132         {});
133     // Return true because we initiated setup.
134     return true;
135   }
136 
GetCloudId() const137   std::string GetCloudId() const override {
138     return connection_state_.status() > ConnectionState::kUnconfigured
139                ? device_->GetSettings().cloud_id
140                : "";
141   }
142 
GetLegacyCommandDef() const143   const base::DictionaryValue& GetLegacyCommandDef() const override {
144     return component_manager_->GetLegacyCommandDefinitions();
145   }
146 
GetLegacyState() const147   const base::DictionaryValue& GetLegacyState() const override {
148     return component_manager_->GetLegacyState();
149   }
150 
GetComponents() const151   const base::DictionaryValue& GetComponents() const override {
152     return component_manager_->GetComponents();
153   }
154 
FindComponent(const std::string & path,ErrorPtr * error) const155   const base::DictionaryValue* FindComponent(const std::string& path,
156                                              ErrorPtr* error) const override {
157     return component_manager_->FindComponent(path, error);
158   }
159 
GetTraits() const160   const base::DictionaryValue& GetTraits() const override {
161     return component_manager_->GetTraits();
162   }
163 
AddCommand(const base::DictionaryValue & command,const UserInfo & user_info,const CommandDoneCallback & callback)164   void AddCommand(const base::DictionaryValue& command,
165                   const UserInfo& user_info,
166                   const CommandDoneCallback& callback) override {
167     CHECK(user_info.scope() != AuthScope::kNone);
168     CHECK(!user_info.id().IsEmpty());
169 
170     ErrorPtr error;
171     UserRole role;
172     std::string str_scope = EnumToString(user_info.scope());
173     if (!StringToEnum(str_scope, &role)) {
174       Error::AddToPrintf(&error, FROM_HERE, errors::kInvalidParams,
175                          "Invalid role: '%s'", str_scope.c_str());
176       return callback.Run({}, std::move(error));
177     }
178 
179     std::string id;
180     auto command_instance = component_manager_->ParseCommandInstance(
181         command, Command::Origin::kLocal, role, &id, &error);
182     if (!command_instance)
183       return callback.Run({}, std::move(error));
184     component_manager_->AddCommand(std::move(command_instance));
185     command_owners_[id] = user_info.id();
186     callback.Run(*component_manager_->FindCommand(id)->ToJson(), nullptr);
187   }
188 
GetCommand(const std::string & id,const UserInfo & user_info,const CommandDoneCallback & callback)189   void GetCommand(const std::string& id,
190                   const UserInfo& user_info,
191                   const CommandDoneCallback& callback) override {
192     CHECK(user_info.scope() != AuthScope::kNone);
193     ErrorPtr error;
194     auto command = GetCommandInternal(id, user_info, &error);
195     if (!command)
196       return callback.Run({}, std::move(error));
197     callback.Run(*command->ToJson(), nullptr);
198   }
199 
CancelCommand(const std::string & id,const UserInfo & user_info,const CommandDoneCallback & callback)200   void CancelCommand(const std::string& id,
201                      const UserInfo& user_info,
202                      const CommandDoneCallback& callback) override {
203     CHECK(user_info.scope() != AuthScope::kNone);
204     ErrorPtr error;
205     auto command = GetCommandInternal(id, user_info, &error);
206     if (!command || !command->Cancel(&error))
207       return callback.Run({}, std::move(error));
208     callback.Run(*command->ToJson(), nullptr);
209   }
210 
ListCommands(const UserInfo & user_info,const CommandDoneCallback & callback)211   void ListCommands(const UserInfo& user_info,
212                     const CommandDoneCallback& callback) override {
213     CHECK(user_info.scope() != AuthScope::kNone);
214 
215     base::ListValue list_value;
216 
217     for (const auto& it : command_owners_) {
218       if (CanAccessCommand(it.second, user_info, nullptr)) {
219         list_value.Append(
220             component_manager_->FindCommand(it.first)->ToJson().release());
221       }
222     }
223 
224     base::DictionaryValue commands_json;
225     commands_json.Set("commands", list_value.DeepCopy());
226 
227     callback.Run(commands_json, nullptr);
228   }
229 
230  private:
OnCommandAdded(Command * command)231   void OnCommandAdded(Command* command) {
232     // Set to "" for any new unknown command.
233     command_owners_.insert(std::make_pair(command->GetID(), UserAppId{}));
234   }
235 
OnCommandRemoved(Command * command)236   void OnCommandRemoved(Command* command) {
237     CHECK(command_owners_.erase(command->GetID()));
238   }
239 
OnConfigChanged(const Settings &)240   void OnConfigChanged(const Settings&) { NotifyOnDeviceInfoChanged(); }
241 
OnRegistrationChanged(GcdState status)242   void OnRegistrationChanged(GcdState status) {
243     if (status == GcdState::kUnconfigured ||
244         status == GcdState::kInvalidCredentials) {
245       connection_state_ = ConnectionState{ConnectionState::kUnconfigured};
246     } else if (status == GcdState::kConnecting) {
247       // TODO(vitalybuka): Find conditions for kOffline.
248       connection_state_ = ConnectionState{ConnectionState::kConnecting};
249     } else if (status == GcdState::kConnected) {
250       connection_state_ = ConnectionState{ConnectionState::kOnline};
251     } else {
252       ErrorPtr error;
253       Error::AddToPrintf(&error, FROM_HERE, errors::kInvalidState,
254                          "Unexpected registration status: %s",
255                          EnumToString(status).c_str());
256       connection_state_ = ConnectionState{std::move(error)};
257     }
258     NotifyOnDeviceInfoChanged();
259   }
260 
OnRegisterSuccess(const std::string & cloud_id)261   void OnRegisterSuccess(const std::string& cloud_id) {
262     VLOG(1) << "Device registered: " << cloud_id;
263     setup_state_ = SetupState(SetupState::kSuccess);
264   }
265 
CallManagerRegisterDevice()266   void CallManagerRegisterDevice() {
267     ErrorPtr error;
268     CHECK_GE(registation_retry_count_, 0);
269     if (registation_retry_count_-- == 0) {
270       Error::AddTo(&error, FROM_HERE, errors::kInvalidState,
271                    "Failed to register device");
272       setup_state_ = SetupState{std::move(error)};
273       return;
274     }
275 
276     device_->RegisterDevice(ticket_id_,
277                             base::Bind(&CloudDelegateImpl::RegisterDeviceDone,
278                                        setup_weak_factory_.GetWeakPtr()));
279   }
280 
RegisterDeviceDone(ErrorPtr error)281   void RegisterDeviceDone(ErrorPtr error) {
282     if (error) {
283       // Registration failed. Retry with backoff.
284       backoff_entry_.InformOfRequest(false);
285       return task_runner_->PostDelayedTask(
286           FROM_HERE, base::Bind(&CloudDelegateImpl::CallManagerRegisterDevice,
287                                 setup_weak_factory_.GetWeakPtr()),
288           backoff_entry_.GetTimeUntilRelease());
289     }
290     backoff_entry_.InformOfRequest(true);
291     setup_state_ = SetupState(SetupState::kSuccess);
292   }
293 
GetCommandInternal(const std::string & command_id,const UserInfo & user_info,ErrorPtr * error) const294   CommandInstance* GetCommandInternal(const std::string& command_id,
295                                       const UserInfo& user_info,
296                                       ErrorPtr* error) const {
297     if (user_info.scope() < AuthScope::kManager) {
298       auto it = command_owners_.find(command_id);
299       if (it == command_owners_.end())
300         return ReturnNotFound(command_id, error);
301       if (CanAccessCommand(it->second, user_info, error))
302         return nullptr;
303     }
304 
305     auto command = component_manager_->FindCommand(command_id);
306     if (!command)
307       return ReturnNotFound(command_id, error);
308 
309     return command;
310   }
311 
CanAccessCommand(const UserAppId & owner,const UserInfo & user_info,ErrorPtr * error) const312   bool CanAccessCommand(const UserAppId& owner,
313                         const UserInfo& user_info,
314                         ErrorPtr* error) const {
315     CHECK(user_info.scope() != AuthScope::kNone);
316     CHECK(!user_info.id().IsEmpty());
317 
318     if (user_info.scope() == AuthScope::kManager ||
319         (owner.type == user_info.id().type &&
320          owner.user == user_info.id().user &&
321          (user_info.id().app.empty() ||  // Token is not restricted to the app.
322           owner.app == user_info.id().app))) {
323       return true;
324     }
325 
326     return Error::AddTo(error, FROM_HERE, errors::kAccessDenied,
327                         "Need to be owner of the command.");
328   }
329 
330   provider::TaskRunner* task_runner_{nullptr};
331   DeviceRegistrationInfo* device_{nullptr};
332   ComponentManager* component_manager_{nullptr};
333 
334   // Primary state of GCD.
335   ConnectionState connection_state_{ConnectionState::kDisabled};
336 
337   // State of the current or last setup.
338   SetupState setup_state_{SetupState::kNone};
339 
340   // Ticket ID for registering the device.
341   std::string ticket_id_;
342 
343   // Number of remaining retries for device registration process.
344   int registation_retry_count_{0};
345 
346   // Map of command IDs to user IDs.
347   std::map<std::string, UserAppId> command_owners_;
348 
349   // Backoff entry for retrying device registration.
350   BackoffEntry backoff_entry_{&register_backoff_policy};
351 
352   // |setup_weak_factory_| tracks the lifetime of callbacks used in connection
353   // with a particular invocation of Setup().
354   base::WeakPtrFactory<CloudDelegateImpl> setup_weak_factory_{this};
355   // |weak_factory_| tracks the lifetime of |this|.
356   base::WeakPtrFactory<CloudDelegateImpl> weak_factory_{this};
357 };
358 
359 }  // namespace
360 
CloudDelegate()361 CloudDelegate::CloudDelegate() {}
362 
~CloudDelegate()363 CloudDelegate::~CloudDelegate() {}
364 
365 // static
CreateDefault(provider::TaskRunner * task_runner,DeviceRegistrationInfo * device,ComponentManager * component_manager)366 std::unique_ptr<CloudDelegate> CloudDelegate::CreateDefault(
367     provider::TaskRunner* task_runner,
368     DeviceRegistrationInfo* device,
369     ComponentManager* component_manager) {
370   return std::unique_ptr<CloudDelegateImpl>{
371       new CloudDelegateImpl{task_runner, device, component_manager}};
372 }
373 
NotifyOnDeviceInfoChanged()374 void CloudDelegate::NotifyOnDeviceInfoChanged() {
375   FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceInfoChanged());
376 }
377 
NotifyOnTraitDefsChanged()378 void CloudDelegate::NotifyOnTraitDefsChanged() {
379   FOR_EACH_OBSERVER(Observer, observer_list_, OnTraitDefsChanged());
380 }
381 
NotifyOnComponentTreeChanged()382 void CloudDelegate::NotifyOnComponentTreeChanged() {
383   FOR_EACH_OBSERVER(Observer, observer_list_, OnComponentTreeChanged());
384 }
385 
NotifyOnStateChanged()386 void CloudDelegate::NotifyOnStateChanged() {
387   FOR_EACH_OBSERVER(Observer, observer_list_, OnStateChanged());
388 }
389 
390 }  // namespace privet
391 }  // namespace weave
392