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 31 namespace base { 32 class DictionaryValue; 33 } // namespace base 34 35 namespace weave { 36 37 class StateManager; 38 39 namespace provider { 40 class Network; 41 class TaskRunner; 42 } 43 44 namespace privet { 45 class AuthManager; 46 } 47 48 // The DeviceRegistrationInfo class represents device registration information. 49 class 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. GetSettings()119 const Config::Settings& GetSettings() const { return config_->GetSettings(); } GetMutableConfig()120 Config* GetMutableConfig() { return config_; } 121 GetGcdState()122 GcdState GetGcdState() const { return gcd_state_; } 123 124 private: 125 friend class DeviceRegistrationInfoTest; 126 AsWeakPtr()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