|  | // Copyright 2015 The Weave Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #ifndef LIBWEAVE_SRC_DEVICE_REGISTRATION_INFO_H_ | 
|  | #define LIBWEAVE_SRC_DEVICE_REGISTRATION_INFO_H_ | 
|  |  | 
|  | #include <map> | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include <base/callback.h> | 
|  | #include <base/macros.h> | 
|  | #include <base/memory/weak_ptr.h> | 
|  | #include <base/time/time.h> | 
|  | #include <weave/device.h> | 
|  | #include <weave/error.h> | 
|  | #include <weave/provider/http_client.h> | 
|  |  | 
|  | #include "src/backoff_entry.h" | 
|  | #include "src/commands/cloud_command_update_interface.h" | 
|  | #include "src/component_manager.h" | 
|  | #include "src/config.h" | 
|  | #include "src/data_encoding.h" | 
|  | #include "src/notification/notification_channel.h" | 
|  | #include "src/notification/notification_delegate.h" | 
|  | #include "src/notification/pull_channel.h" | 
|  |  | 
|  | namespace base { | 
|  | class DictionaryValue; | 
|  | }  // namespace base | 
|  |  | 
|  | namespace weave { | 
|  |  | 
|  | class StateManager; | 
|  |  | 
|  | namespace provider { | 
|  | class Network; | 
|  | class TaskRunner; | 
|  | } | 
|  |  | 
|  | namespace privet { | 
|  | class AuthManager; | 
|  | } | 
|  |  | 
|  | // The DeviceRegistrationInfo class represents device registration information. | 
|  | class DeviceRegistrationInfo : public NotificationDelegate, | 
|  | public CloudCommandUpdateInterface { | 
|  | public: | 
|  | using CloudRequestDoneCallback = | 
|  | base::Callback<void(const base::DictionaryValue& response, | 
|  | ErrorPtr error)>; | 
|  |  | 
|  | DeviceRegistrationInfo(Config* config, | 
|  | ComponentManager* component_manager, | 
|  | provider::TaskRunner* task_runner, | 
|  | provider::HttpClient* http_client, | 
|  | provider::Network* network, | 
|  | privet::AuthManager* auth_manager); | 
|  |  | 
|  | ~DeviceRegistrationInfo() override; | 
|  |  | 
|  | void AddGcdStateChangedCallback( | 
|  | const Device::GcdStateChangedCallback& callback); | 
|  |  | 
|  | void RegisterDevice(RegistrationData registration_data, | 
|  | const DoneCallback& callback); | 
|  |  | 
|  | void UpdateDeviceInfo(const std::string& name, | 
|  | const std::string& description, | 
|  | const std::string& location); | 
|  | void UpdateBaseConfig(AuthScope anonymous_access_role, | 
|  | bool local_discovery_enabled, | 
|  | bool local_pairing_enabled); | 
|  |  | 
|  | void GetDeviceInfo(const CloudRequestDoneCallback& callback); | 
|  |  | 
|  | // Returns the GCD service request URL. If |subpath| is specified, it is | 
|  | // appended to the base URL which is normally | 
|  | //    https://www.googleapis.com/weave/v1/". | 
|  | // If |params| are specified, each key-value pair is formatted using | 
|  | // WebParamsEncode() and appended to URL as a query | 
|  | // string. | 
|  | // So, calling: | 
|  | //    GetServiceUrl("ticket", {{"key","apiKey"}}) | 
|  | // will return something like: | 
|  | //    https://www.googleapis.com/weave/v1/ticket?key=apiKey | 
|  | std::string GetServiceUrl(const std::string& subpath = {}, | 
|  | const WebParamList& params = {}) const; | 
|  |  | 
|  | // Returns a service URL to access the registered device on GCD server. | 
|  | // The base URL used to construct the full URL looks like this: | 
|  | //    https://www.googleapis.com/weave/v1/devices/<cloud_id>/ | 
|  | std::string GetDeviceUrl(const std::string& subpath = {}, | 
|  | const WebParamList& params = {}) const; | 
|  |  | 
|  | // Similar to GetServiceURL, GetOAuthURL() returns a URL of OAuth 2.0 server. | 
|  | // The base URL used is https://accounts.google.com/o/oauth2/. | 
|  | std::string GetOAuthUrl(const std::string& subpath = {}, | 
|  | const WebParamList& params = {}) const; | 
|  |  | 
|  | // Starts GCD device if credentials available. | 
|  | void Start(); | 
|  |  | 
|  | // Updates a command (override from CloudCommandUpdateInterface). | 
|  | void UpdateCommand(const std::string& command_id, | 
|  | const base::DictionaryValue& command_patch, | 
|  | const DoneCallback& callback) override; | 
|  |  | 
|  | // TODO(vitalybuka): remove getters and pass config to dependent code. | 
|  | const Config::Settings& GetSettings() const { return config_->GetSettings(); } | 
|  | Config* GetMutableConfig() { return config_; } | 
|  |  | 
|  | GcdState GetGcdState() const { return gcd_state_; } | 
|  |  | 
|  | // Checks whether we have credentials generated during registration. | 
|  | bool HaveRegistrationCredentials() const; | 
|  |  | 
|  | private: | 
|  | friend class DeviceRegistrationInfoTest; | 
|  |  | 
|  | const Config::Settings& GetDefaults() const { return config_->GetDefaults(); } | 
|  |  | 
|  | base::WeakPtr<DeviceRegistrationInfo> AsWeakPtr() { | 
|  | return weak_factory_.GetWeakPtr(); | 
|  | } | 
|  |  | 
|  | // Calls HaveRegistrationCredentials() and logs an error if no credentials | 
|  | // are available. | 
|  | bool VerifyRegistrationCredentials(ErrorPtr* error) const; | 
|  |  | 
|  | // Cause DeviceRegistrationInfo to attempt to connect to cloud server on | 
|  | // its own later. | 
|  | void ScheduleCloudConnection(const base::TimeDelta& delay); | 
|  |  | 
|  | // Initiates the connection to the cloud server. | 
|  | // Device will do required start up chores and then start to listen | 
|  | // to new commands. | 
|  | void ConnectToCloud(ErrorPtr error); | 
|  | // Notification called when ConnectToCloud() succeeds. | 
|  | void OnConnectedToCloud(ErrorPtr error); | 
|  |  | 
|  | // Forcibly refreshes the access token. | 
|  | void RefreshAccessToken(const DoneCallback& callback); | 
|  |  | 
|  | // Callbacks for RefreshAccessToken(). | 
|  | void OnRefreshAccessTokenDone( | 
|  | const DoneCallback& callback, | 
|  | std::unique_ptr<provider::HttpClient::Response> response, | 
|  | ErrorPtr error); | 
|  |  | 
|  | // Parse the OAuth response, and sets registration status to | 
|  | // kInvalidCredentials if our registration is no longer valid. | 
|  | std::unique_ptr<base::DictionaryValue> ParseOAuthResponse( | 
|  | const provider::HttpClient::Response& response, | 
|  | ErrorPtr* error); | 
|  |  | 
|  | // This attempts to open a notification channel. The channel needs to be | 
|  | // restarted anytime the access_token is refreshed. | 
|  | void StartNotificationChannel(); | 
|  |  | 
|  | // Helpers to start and stop pull notification channel. | 
|  | void StartPullChannel(); | 
|  | void StopPullChannel(); | 
|  |  | 
|  | // Do a HTTPS request to cloud services. | 
|  | // Handles many cases like reauthorization, 5xx HTTP response codes | 
|  | // and device removal.  It is a recommended way to do cloud API | 
|  | // requests. | 
|  | // TODO(antonm): Consider moving into some other class. | 
|  | void DoCloudRequest(provider::HttpClient::Method method, | 
|  | const std::string& url, | 
|  | const base::DictionaryValue* body, | 
|  | const CloudRequestDoneCallback& callback); | 
|  |  | 
|  | // Helper for DoCloudRequest(). | 
|  | struct CloudRequestData { | 
|  | provider::HttpClient::Method method; | 
|  | std::string url; | 
|  | std::string body; | 
|  | CloudRequestDoneCallback callback; | 
|  | }; | 
|  | void SendCloudRequest(const std::shared_ptr<const CloudRequestData>& data); | 
|  | void OnCloudRequestDone( | 
|  | const std::shared_ptr<const CloudRequestData>& data, | 
|  | std::unique_ptr<provider::HttpClient::Response> response, | 
|  | ErrorPtr error); | 
|  | void RetryCloudRequest(const std::shared_ptr<const CloudRequestData>& data); | 
|  | void OnAccessTokenRefreshed( | 
|  | const std::shared_ptr<const CloudRequestData>& data, | 
|  | ErrorPtr error); | 
|  | void CheckAccessTokenError(ErrorPtr error); | 
|  |  | 
|  | void UpdateDeviceResource(const DoneCallback& callback); | 
|  | void StartQueuedUpdateDeviceResource(); | 
|  | void OnUpdateDeviceResourceDone(const base::DictionaryValue& device_info, | 
|  | ErrorPtr error); | 
|  | void OnUpdateDeviceResourceError(ErrorPtr error); | 
|  |  | 
|  | void SendAuthInfo(); | 
|  | void OnSendAuthInfoDone(const std::vector<uint8_t>& token, | 
|  | const base::DictionaryValue& body, | 
|  | ErrorPtr error); | 
|  |  | 
|  | // Callback from GetDeviceInfo() to retrieve the device resource timestamp | 
|  | // and retry UpdateDeviceResource() call. | 
|  | void OnDeviceInfoRetrieved(const base::DictionaryValue& device_info, | 
|  | ErrorPtr error); | 
|  |  | 
|  | // Extracts the timestamp from the device resource and sets it to | 
|  | // |last_device_resource_updated_timestamp_|. | 
|  | // Returns false if the "lastUpdateTimeMs" field is not found in the device | 
|  | // resource or it is invalid. | 
|  | bool UpdateDeviceInfoTimestamp(const base::DictionaryValue& device_info); | 
|  |  | 
|  | void FetchCommands( | 
|  | const base::Callback<void(const base::ListValue&, ErrorPtr)>& callback, | 
|  | const std::string& reason); | 
|  | void OnFetchCommandsDone( | 
|  | const base::Callback<void(const base::ListValue&, ErrorPtr)>& callback, | 
|  | const base::DictionaryValue& json, | 
|  | ErrorPtr); | 
|  | // Called when FetchCommands completes (with either success or error). | 
|  | // This method reschedules any pending/queued fetch requests. | 
|  | void OnFetchCommandsReturned(); | 
|  |  | 
|  | // Processes the command list that is fetched from the server on connection. | 
|  | // Aborts commands which are in transitional states and publishes queued | 
|  | // commands which are queued. | 
|  | void ProcessInitialCommandList(const base::ListValue& commands, | 
|  | ErrorPtr error); | 
|  |  | 
|  | void PublishCommands(const base::ListValue& commands, ErrorPtr error); | 
|  | void PublishCommand(const base::DictionaryValue& command); | 
|  |  | 
|  | // Helper function to pull the pending command list from the server using | 
|  | // FetchCommands() and make them available on D-Bus with PublishCommands(). | 
|  | // |backup_fetch| is set to true when performing backup ("just-in-case") | 
|  | // command fetch while XMPP channel is up and running. | 
|  | void FetchAndPublishCommands(const std::string& reason); | 
|  |  | 
|  | void PublishStateUpdates(); | 
|  | void OnPublishStateDone(ComponentManager::UpdateID update_id, | 
|  | const base::DictionaryValue& reply, | 
|  | ErrorPtr error); | 
|  | void OnPublishStateError(ErrorPtr error); | 
|  |  | 
|  | // If unrecoverable error occurred (e.g. error parsing command instance), | 
|  | // notify the server that the command is aborted by the device. | 
|  | void NotifyCommandAborted(const std::string& command_id, ErrorPtr error); | 
|  |  | 
|  | // Builds Cloud API devices collection REST resource which matches | 
|  | // current state of the device including command definitions | 
|  | // for all supported commands and current device state. | 
|  | std::unique_ptr<base::DictionaryValue> BuildDeviceResource() const; | 
|  |  | 
|  | void SetGcdState(GcdState new_state); | 
|  | void SetDeviceId(const std::string& cloud_id); | 
|  |  | 
|  | // Callback called when command definitions are changed to re-publish new CDD. | 
|  | void OnTraitDefsChanged(); | 
|  | void OnComponentTreeChanged(); | 
|  | void OnStateChanged(); | 
|  |  | 
|  | // Overrides from NotificationDelegate. | 
|  | void OnConnected(const std::string& channel_name) override; | 
|  | void OnDisconnected() override; | 
|  | void OnPermanentFailure() override; | 
|  | void OnCommandCreated(const base::DictionaryValue& command, | 
|  | const std::string& channel_name) override; | 
|  | void OnDeviceDeleted(const std::string& cloud_id) override; | 
|  |  | 
|  | // Wipes out the device registration information and stops server connections. | 
|  | void RemoveCredentials(); | 
|  |  | 
|  | void RegisterDeviceError(const DoneCallback& callback, ErrorPtr error); | 
|  | void RegisterDeviceOnTicketSent( | 
|  | const RegistrationData& registration_data, | 
|  | const DoneCallback& callback, | 
|  | std::unique_ptr<provider::HttpClient::Response> response, | 
|  | ErrorPtr error); | 
|  | void RegisterDeviceOnTicketFinalized( | 
|  | const RegistrationData& registration_data, | 
|  | const DoneCallback& callback, | 
|  | std::unique_ptr<provider::HttpClient::Response> response, | 
|  | ErrorPtr error); | 
|  | void RegisterDeviceOnAuthCodeSent( | 
|  | const RegistrationData& registration_data, | 
|  | const std::string& cloud_id, | 
|  | const std::string& robot_account, | 
|  | const DoneCallback& callback, | 
|  | std::unique_ptr<provider::HttpClient::Response> response, | 
|  | ErrorPtr error); | 
|  |  | 
|  | // Transient data | 
|  | std::string access_token_; | 
|  | base::Time access_token_expiration_; | 
|  | // The time stamp of last device resource update on the server. | 
|  | std::string last_device_resource_updated_timestamp_; | 
|  | // Set to true if the device has connected to the cloud server correctly. | 
|  | // At this point, normal state and command updates can be dispatched to the | 
|  | // server. | 
|  | bool connected_to_cloud_{false}; | 
|  |  | 
|  | // HTTP transport used for communications. | 
|  | provider::HttpClient* http_client_{nullptr}; | 
|  |  | 
|  | provider::TaskRunner* task_runner_{nullptr}; | 
|  |  | 
|  | Config* config_{nullptr}; | 
|  |  | 
|  | // Global component manager. | 
|  | ComponentManager* component_manager_{nullptr}; | 
|  |  | 
|  | // Backoff manager for DoCloudRequest() method. | 
|  | std::unique_ptr<BackoffEntry::Policy> cloud_backoff_policy_; | 
|  | std::unique_ptr<BackoffEntry> cloud_backoff_entry_; | 
|  | std::unique_ptr<BackoffEntry> oauth2_backoff_entry_; | 
|  |  | 
|  | // Flag set to true while a device state update patch request is in flight | 
|  | // to the cloud server. | 
|  | bool device_state_update_pending_{false}; | 
|  |  | 
|  | // Set to true when command queue fetch request is in flight to the server. | 
|  | bool fetch_commands_request_sent_{false}; | 
|  | // Set to true when another command queue fetch request is queued while | 
|  | // another one was in flight. | 
|  | bool fetch_commands_request_queued_{false}; | 
|  | // Specifies the reason for queued command fetch request. | 
|  | std::string queued_fetch_reason_; | 
|  |  | 
|  | using ResourceUpdateCallbackList = std::vector<DoneCallback>; | 
|  | // Callbacks for device resource update request currently in flight to the | 
|  | // cloud server. | 
|  | ResourceUpdateCallbackList in_progress_resource_update_callbacks_; | 
|  | // Callbacks for device resource update requests queued while another request | 
|  | // is in flight to the cloud server. | 
|  | ResourceUpdateCallbackList queued_resource_update_callbacks_; | 
|  |  | 
|  | bool auth_info_update_inprogress_{false}; | 
|  |  | 
|  | std::unique_ptr<NotificationChannel> primary_notification_channel_; | 
|  | std::unique_ptr<PullChannel> pull_channel_; | 
|  | NotificationChannel* current_notification_channel_{nullptr}; | 
|  | bool notification_channel_starting_{false}; | 
|  |  | 
|  | provider::Network* network_{nullptr}; | 
|  | privet::AuthManager* auth_manager_{nullptr}; | 
|  |  | 
|  | // Tracks our GCD state. | 
|  | GcdState gcd_state_{GcdState::kUnconfigured}; | 
|  |  | 
|  | std::vector<Device::GcdStateChangedCallback> gcd_state_changed_callbacks_; | 
|  |  | 
|  | base::WeakPtrFactory<DeviceRegistrationInfo> weak_factory_{this}; | 
|  | DISALLOW_COPY_AND_ASSIGN(DeviceRegistrationInfo); | 
|  | }; | 
|  |  | 
|  | }  // namespace weave | 
|  |  | 
|  | #endif  // LIBWEAVE_SRC_DEVICE_REGISTRATION_INFO_H_ |