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#ifndef LIBWEAVE_SRC_DEVICE_REGISTRATION_INFO_H_
6#define LIBWEAVE_SRC_DEVICE_REGISTRATION_INFO_H_
7
8#include <map>
9#include <memory>
10#include <string>
11#include <utility>
12#include <vector>
13
14#include <base/callback.h>
15#include <base/macros.h>
16#include <base/memory/weak_ptr.h>
17#include <base/time/time.h>
18#include <weave/device.h>
19#include <weave/error.h>
20#include <weave/provider/http_client.h>
21
22#include "src/backoff_entry.h"
23#include "src/commands/cloud_command_update_interface.h"
24#include "src/component_manager.h"
25#include "src/config.h"
26#include "src/data_encoding.h"
27#include "src/notification/notification_channel.h"
28#include "src/notification/notification_delegate.h"
29#include "src/notification/pull_channel.h"
30
31namespace base {
32class DictionaryValue;
33}  // namespace base
34
35namespace weave {
36
37class StateManager;
38
39namespace provider {
40class Network;
41class TaskRunner;
42}
43
44namespace privet {
45class AuthManager;
46}
47
48// The DeviceRegistrationInfo class represents device registration information.
49class DeviceRegistrationInfo : public NotificationDelegate,
50                               public CloudCommandUpdateInterface {
51 public:
52  using CloudRequestDoneCallback =
53      base::Callback<void(const base::DictionaryValue& response,
54                          ErrorPtr error)>;
55
56  DeviceRegistrationInfo(Config* config,
57                         ComponentManager* component_manager,
58                         provider::TaskRunner* task_runner,
59                         provider::HttpClient* http_client,
60                         provider::Network* network,
61                         privet::AuthManager* auth_manager);
62
63  ~DeviceRegistrationInfo() override;
64
65  void AddGcdStateChangedCallback(
66      const Device::GcdStateChangedCallback& callback);
67  void RegisterDevice(const std::string& ticket_id,
68                      const DoneCallback& callback);
69
70  void UpdateDeviceInfo(const std::string& name,
71                        const std::string& description,
72                        const std::string& location);
73  void UpdateBaseConfig(AuthScope anonymous_access_role,
74                        bool local_discovery_enabled,
75                        bool local_pairing_enabled);
76  bool UpdateServiceConfig(const std::string& client_id,
77                           const std::string& client_secret,
78                           const std::string& api_key,
79                           const std::string& oauth_url,
80                           const std::string& service_url,
81                           const std::string& xmpp_endpoint,
82                           ErrorPtr* error);
83
84  void GetDeviceInfo(const CloudRequestDoneCallback& callback);
85
86  // Returns the GCD service request URL. If |subpath| is specified, it is
87  // appended to the base URL which is normally
88  //    https://www.googleapis.com/weave/v1/".
89  // If |params| are specified, each key-value pair is formatted using
90  // WebParamsEncode() and appended to URL as a query
91  // string.
92  // So, calling:
93  //    GetServiceURL("ticket", {{"key","apiKey"}})
94  // will return something like:
95  //    https://www.googleapis.com/weave/v1/ticket?key=apiKey
96  std::string GetServiceURL(const std::string& subpath = {},
97                            const WebParamList& params = {}) const;
98
99  // Returns a service URL to access the registered device on GCD server.
100  // The base URL used to construct the full URL looks like this:
101  //    https://www.googleapis.com/weave/v1/devices/<cloud_id>/
102  std::string GetDeviceURL(const std::string& subpath = {},
103                           const WebParamList& params = {}) const;
104
105  // Similar to GetServiceURL, GetOAuthURL() returns a URL of OAuth 2.0 server.
106  // The base URL used is https://accounts.google.com/o/oauth2/.
107  std::string GetOAuthURL(const std::string& subpath = {},
108                          const WebParamList& params = {}) const;
109
110  // Starts GCD device if credentials available.
111  void Start();
112
113  // Updates a command (override from CloudCommandUpdateInterface).
114  void UpdateCommand(const std::string& command_id,
115                     const base::DictionaryValue& command_patch,
116                     const DoneCallback& callback) override;
117
118  // TODO(vitalybuka): remove getters and pass config to dependent code.
119  const Config::Settings& GetSettings() const { return config_->GetSettings(); }
120  Config* GetMutableConfig() { return config_; }
121
122  GcdState GetGcdState() const { return gcd_state_; }
123
124 private:
125  friend class DeviceRegistrationInfoTest;
126
127  base::WeakPtr<DeviceRegistrationInfo> AsWeakPtr() {
128    return weak_factory_.GetWeakPtr();
129  }
130
131  // Checks whether we have credentials generated during registration.
132  bool HaveRegistrationCredentials() const;
133  // Calls HaveRegistrationCredentials() and logs an error if no credentials
134  // are available.
135  bool VerifyRegistrationCredentials(ErrorPtr* error) const;
136
137  // Cause DeviceRegistrationInfo to attempt to connect to cloud server on
138  // its own later.
139  void ScheduleCloudConnection(const base::TimeDelta& delay);
140
141  // Initiates the connection to the cloud server.
142  // Device will do required start up chores and then start to listen
143  // to new commands.
144  void ConnectToCloud(ErrorPtr error);
145  // Notification called when ConnectToCloud() succeeds.
146  void OnConnectedToCloud(ErrorPtr error);
147
148  // Forcibly refreshes the access token.
149  void RefreshAccessToken(const DoneCallback& callback);
150
151  // Callbacks for RefreshAccessToken().
152  void OnRefreshAccessTokenDone(
153      const DoneCallback& callback,
154      std::unique_ptr<provider::HttpClient::Response> response,
155      ErrorPtr error);
156
157  // Parse the OAuth response, and sets registration status to
158  // kInvalidCredentials if our registration is no longer valid.
159  std::unique_ptr<base::DictionaryValue> ParseOAuthResponse(
160      const provider::HttpClient::Response& response,
161      ErrorPtr* error);
162
163  // This attempts to open a notification channel. The channel needs to be
164  // restarted anytime the access_token is refreshed.
165  void StartNotificationChannel();
166
167  // Do a HTTPS request to cloud services.
168  // Handles many cases like reauthorization, 5xx HTTP response codes
169  // and device removal.  It is a recommended way to do cloud API
170  // requests.
171  // TODO(antonm): Consider moving into some other class.
172  void DoCloudRequest(provider::HttpClient::Method method,
173                      const std::string& url,
174                      const base::DictionaryValue* body,
175                      const CloudRequestDoneCallback& callback);
176
177  // Helper for DoCloudRequest().
178  struct CloudRequestData {
179    provider::HttpClient::Method method;
180    std::string url;
181    std::string body;
182    CloudRequestDoneCallback callback;
183  };
184  void SendCloudRequest(const std::shared_ptr<const CloudRequestData>& data);
185  void OnCloudRequestDone(
186      const std::shared_ptr<const CloudRequestData>& data,
187      std::unique_ptr<provider::HttpClient::Response> response,
188      ErrorPtr error);
189  void RetryCloudRequest(const std::shared_ptr<const CloudRequestData>& data);
190  void OnAccessTokenRefreshed(
191      const std::shared_ptr<const CloudRequestData>& data,
192      ErrorPtr error);
193  void CheckAccessTokenError(ErrorPtr error);
194
195  void UpdateDeviceResource(const DoneCallback& callback);
196  void StartQueuedUpdateDeviceResource();
197  void OnUpdateDeviceResourceDone(const base::DictionaryValue& device_info,
198                                  ErrorPtr error);
199  void OnUpdateDeviceResourceError(ErrorPtr error);
200
201  void SendAuthInfo();
202  void OnSendAuthInfoDone(const std::vector<uint8_t>& token,
203                          const base::DictionaryValue& body,
204                          ErrorPtr error);
205
206  // Callback from GetDeviceInfo() to retrieve the device resource timestamp
207  // and retry UpdateDeviceResource() call.
208  void OnDeviceInfoRetrieved(const base::DictionaryValue& device_info,
209                             ErrorPtr error);
210
211  // Extracts the timestamp from the device resource and sets it to
212  // |last_device_resource_updated_timestamp_|.
213  // Returns false if the "lastUpdateTimeMs" field is not found in the device
214  // resource or it is invalid.
215  bool UpdateDeviceInfoTimestamp(const base::DictionaryValue& device_info);
216
217  void FetchCommands(
218      const base::Callback<void(const base::ListValue&, ErrorPtr)>& callback,
219      const std::string& reason);
220  void OnFetchCommandsDone(
221      const base::Callback<void(const base::ListValue&, ErrorPtr)>& callback,
222      const base::DictionaryValue& json,
223      ErrorPtr);
224  // Called when FetchCommands completes (with either success or error).
225  // This method reschedules any pending/queued fetch requests.
226  void OnFetchCommandsReturned();
227
228  // Processes the command list that is fetched from the server on connection.
229  // Aborts commands which are in transitional states and publishes queued
230  // commands which are queued.
231  void ProcessInitialCommandList(const base::ListValue& commands,
232                                 ErrorPtr error);
233
234  void PublishCommands(const base::ListValue& commands, ErrorPtr error);
235  void PublishCommand(const base::DictionaryValue& command);
236
237  // Helper function to pull the pending command list from the server using
238  // FetchCommands() and make them available on D-Bus with PublishCommands().
239  // |backup_fetch| is set to true when performing backup ("just-in-case")
240  // command fetch while XMPP channel is up and running.
241  void FetchAndPublishCommands(const std::string& reason);
242
243  void PublishStateUpdates();
244  void OnPublishStateDone(ComponentManager::UpdateID update_id,
245                          const base::DictionaryValue& reply,
246                          ErrorPtr error);
247  void OnPublishStateError(ErrorPtr error);
248
249  // If unrecoverable error occurred (e.g. error parsing command instance),
250  // notify the server that the command is aborted by the device.
251  void NotifyCommandAborted(const std::string& command_id, ErrorPtr error);
252
253  // Builds Cloud API devices collection REST resource which matches
254  // current state of the device including command definitions
255  // for all supported commands and current device state.
256  std::unique_ptr<base::DictionaryValue> BuildDeviceResource() const;
257
258  void SetGcdState(GcdState new_state);
259  void SetDeviceId(const std::string& cloud_id);
260
261  // Callback called when command definitions are changed to re-publish new CDD.
262  void OnTraitDefsChanged();
263  void OnComponentTreeChanged();
264  void OnStateChanged();
265
266  // Overrides from NotificationDelegate.
267  void OnConnected(const std::string& channel_name) override;
268  void OnDisconnected() override;
269  void OnPermanentFailure() override;
270  void OnCommandCreated(const base::DictionaryValue& command,
271                        const std::string& channel_name) override;
272  void OnDeviceDeleted(const std::string& cloud_id) override;
273
274  // Wipes out the device registration information and stops server connections.
275  void RemoveCredentials();
276
277  void RegisterDeviceError(const DoneCallback& callback, ErrorPtr error);
278  void RegisterDeviceOnTicketSent(
279      const std::string& ticket_id,
280      const DoneCallback& callback,
281      std::unique_ptr<provider::HttpClient::Response> response,
282      ErrorPtr error);
283  void RegisterDeviceOnTicketFinalized(
284      const DoneCallback& callback,
285      std::unique_ptr<provider::HttpClient::Response> response,
286      ErrorPtr error);
287  void RegisterDeviceOnAuthCodeSent(
288      const std::string& cloud_id,
289      const std::string& robot_account,
290      const DoneCallback& callback,
291      std::unique_ptr<provider::HttpClient::Response> response,
292      ErrorPtr error);
293
294  // Transient data
295  std::string access_token_;
296  base::Time access_token_expiration_;
297  // The time stamp of last device resource update on the server.
298  std::string last_device_resource_updated_timestamp_;
299  // Set to true if the device has connected to the cloud server correctly.
300  // At this point, normal state and command updates can be dispatched to the
301  // server.
302  bool connected_to_cloud_{false};
303
304  // HTTP transport used for communications.
305  provider::HttpClient* http_client_{nullptr};
306
307  provider::TaskRunner* task_runner_{nullptr};
308
309  Config* config_{nullptr};
310
311  // Global component manager.
312  ComponentManager* component_manager_{nullptr};
313
314  // Backoff manager for DoCloudRequest() method.
315  std::unique_ptr<BackoffEntry::Policy> cloud_backoff_policy_;
316  std::unique_ptr<BackoffEntry> cloud_backoff_entry_;
317  std::unique_ptr<BackoffEntry> oauth2_backoff_entry_;
318
319  // Flag set to true while a device state update patch request is in flight
320  // to the cloud server.
321  bool device_state_update_pending_{false};
322
323  // Set to true when command queue fetch request is in flight to the server.
324  bool fetch_commands_request_sent_{false};
325  // Set to true when another command queue fetch request is queued while
326  // another one was in flight.
327  bool fetch_commands_request_queued_{false};
328  // Specifies the reason for queued command fetch request.
329  std::string queued_fetch_reason_;
330
331  using ResourceUpdateCallbackList = std::vector<DoneCallback>;
332  // Callbacks for device resource update request currently in flight to the
333  // cloud server.
334  ResourceUpdateCallbackList in_progress_resource_update_callbacks_;
335  // Callbacks for device resource update requests queued while another request
336  // is in flight to the cloud server.
337  ResourceUpdateCallbackList queued_resource_update_callbacks_;
338
339  bool auth_info_update_inprogress_{false};
340
341  std::unique_ptr<NotificationChannel> primary_notification_channel_;
342  std::unique_ptr<PullChannel> pull_channel_;
343  NotificationChannel* current_notification_channel_{nullptr};
344  bool notification_channel_starting_{false};
345
346  provider::Network* network_{nullptr};
347  privet::AuthManager* auth_manager_{nullptr};
348
349  // Tracks our GCD state.
350  GcdState gcd_state_{GcdState::kUnconfigured};
351
352  std::vector<Device::GcdStateChangedCallback> gcd_state_changed_callbacks_;
353
354  base::WeakPtrFactory<DeviceRegistrationInfo> weak_factory_{this};
355  DISALLOW_COPY_AND_ASSIGN(DeviceRegistrationInfo);
356};
357
358}  // namespace weave
359
360#endif  // LIBWEAVE_SRC_DEVICE_REGISTRATION_INFO_H_
361