blob: 43c70a43ea74ac7af3252ecd866fbf914323a000 [file] [log] [blame]
Alex Vakulenko3cb466c2014-04-15 11:36:32 -07001// Copyright 2014 The Chromium OS 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
Alex Vakulenkob3aac252014-05-07 17:35:24 -07005#ifndef BUFFET_DEVICE_REGISTRATION_INFO_H_
6#define BUFFET_DEVICE_REGISTRATION_INFO_H_
Alex Vakulenko3cb466c2014-04-15 11:36:32 -07007
Alex Vakulenko3cb466c2014-04-15 11:36:32 -07008#include <map>
9#include <memory>
Alex Vakulenko5841c302014-07-23 10:49:49 -070010#include <string>
Alex Vakulenkob3aac252014-05-07 17:35:24 -070011#include <utility>
Vitaly Bukaee7a3af2015-05-14 16:57:23 -070012#include <vector>
Alex Vakulenko3cb466c2014-04-15 11:36:32 -070013
Alex Vakulenko266b2b12015-06-19 11:01:42 -070014#include <base/callback.h>
Alex Vakulenko132617a2014-09-04 08:59:43 -070015#include <base/macros.h>
Christopher Wileycd419662015-02-06 17:51:43 -080016#include <base/memory/weak_ptr.h>
Alex Vakulenkoa3062c52014-04-21 17:05:51 -070017#include <base/time/time.h>
Christopher Wiley34eae042015-03-18 10:25:08 -070018#include <base/timer/timer.h>
Alex Vakulenko266b2b12015-06-19 11:01:42 -070019#include <chromeos/backoff_entry.h>
Alex Vakulenkoe4879a22014-08-20 15:47:36 -070020#include <chromeos/data_encoding.h>
Alex Vakulenkoa8b95bc2014-08-27 11:00:57 -070021#include <chromeos/errors/error.h>
22#include <chromeos/http/http_transport.h>
Alex Vakulenkoa3062c52014-04-21 17:05:51 -070023
Christopher Wiley583d64b2015-03-24 14:30:17 -070024#include "buffet/buffet_config.h"
Alex Vakulenko9ea5a322015-04-17 15:35:34 -070025#include "buffet/commands/command_manager.h"
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070026#include "buffet/notification/notification_channel.h"
27#include "buffet/notification/notification_delegate.h"
Alex Vakulenkod05725f2015-05-27 15:48:19 -070028#include "buffet/notification/pull_channel.h"
Christopher Wileyc900e482015-02-15 15:42:04 -080029#include "buffet/registration_status.h"
Christopher Wiley006e94e2014-05-02 13:44:48 -070030#include "buffet/storage_interface.h"
Alex Vakulenko3cb466c2014-04-15 11:36:32 -070031
32namespace base {
Vitaly Buka6ca3ad62015-03-11 17:03:23 -070033class DictionaryValue;
Alex Vakulenko3cb466c2014-04-15 11:36:32 -070034} // namespace base
35
Anton Muhin332df192014-11-22 05:59:14 +040036namespace chromeos {
37class KeyValueStore;
38} // namespace chromeos
39
Vitaly Buka63cc3d22015-06-23 20:11:36 -070040namespace privetd {
41class ShillClient;
42}
43
Alex Vakulenko3cb466c2014-04-15 11:36:32 -070044namespace buffet {
45
Alex Vakulenko07216fe2014-09-19 15:31:09 -070046class StateManager;
Alex Vakulenko1f30a622014-07-23 11:13:15 -070047
Alex Vakulenkob3aac252014-05-07 17:35:24 -070048extern const char kErrorDomainOAuth2[];
49extern const char kErrorDomainGCD[];
50extern const char kErrorDomainGCDServer[];
Alex Vakulenkob3aac252014-05-07 17:35:24 -070051
Alex Vakulenko3cb466c2014-04-15 11:36:32 -070052// The DeviceRegistrationInfo class represents device registration information.
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -070053class DeviceRegistrationInfo : public NotificationDelegate {
Alex Vakulenko3cb466c2014-04-15 11:36:32 -070054 public:
Vitaly Bukaee7a3af2015-05-14 16:57:23 -070055 using OnRegistrationChangedCallback =
56 base::Callback<void(RegistrationStatus)>;
Alex Vakulenko266b2b12015-06-19 11:01:42 -070057 using CloudRequestCallback =
58 base::Callback<void(const base::DictionaryValue&)>;
59 using CloudRequestErrorCallback =
60 base::Callback<void(const chromeos::Error* error)>;
Christopher Wileye0fdeee2015-02-07 18:29:32 -080061
Alex Vakulenkocca20932014-08-20 17:35:12 -070062 DeviceRegistrationInfo(
63 const std::shared_ptr<CommandManager>& command_manager,
Anton Muhinb8315622014-11-20 03:17:05 +040064 const std::shared_ptr<StateManager>& state_manager,
Vitaly Buka867b0882015-04-16 10:03:26 -070065 std::unique_ptr<BuffetConfig> config,
Alex Vakulenkocca20932014-08-20 17:35:12 -070066 const std::shared_ptr<chromeos::http::Transport>& transport,
Vitaly Buka63cc3d22015-06-23 20:11:36 -070067 bool notifications_enabled,
68 privetd::ShillClient* shill_client);
Alex Vakulenko3cb466c2014-04-15 11:36:32 -070069
Alex Vakulenko534a3122015-05-22 15:48:53 -070070 ~DeviceRegistrationInfo() override;
Anton Muhin332df192014-11-22 05:59:14 +040071
Vitaly Bukaee7a3af2015-05-14 16:57:23 -070072 // Add callback to listen for changes in registration status.
73 void AddOnRegistrationChangedCallback(
74 const OnRegistrationChangedCallback& callback);
Christopher Wileyc900e482015-02-15 15:42:04 -080075
Vitaly Buka7ab89ff2015-06-09 22:11:40 -070076 // Add callback to listen for changes in config.
77 void AddOnConfigChangedCallback(
78 const BuffetConfig::OnChangedCallback& callback);
79
Alex Vakulenko3cb466c2014-04-15 11:36:32 -070080 // Returns the authorization HTTP header that can be used to talk
81 // to GCD server for authenticated device communication.
Alex Vakulenko8e34d392014-04-29 11:02:56 -070082 // Make sure ValidateAndRefreshAccessToken() is called before this call.
Alex Vakulenko3cb466c2014-04-15 11:36:32 -070083 std::pair<std::string, std::string> GetAuthorizationHeader() const;
84
85 // Returns the GCD service request URL. If |subpath| is specified, it is
86 // appended to the base URL which is normally
87 // https://www.googleapis.com/clouddevices/v1/".
88 // If |params| are specified, each key-value pair is formatted using
Alex Vakulenkoe4879a22014-08-20 15:47:36 -070089 // chromeos::data_encoding::WebParamsEncode() and appended to URL as a query
Alex Vakulenko3cb466c2014-04-15 11:36:32 -070090 // string.
91 // So, calling:
92 // GetServiceURL("ticket", {{"key","apiKey"}})
93 // will return something like:
94 // https://www.googleapis.com/clouddevices/v1/ticket?key=apiKey
95 std::string GetServiceURL(
96 const std::string& subpath = {},
Alex Vakulenkoe4879a22014-08-20 15:47:36 -070097 const chromeos::data_encoding::WebParamList& params = {}) const;
Alex Vakulenko3cb466c2014-04-15 11:36:32 -070098
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/clouddevices/v1/devices/<device_id>/
102 std::string GetDeviceURL(
103 const std::string& subpath = {},
Alex Vakulenkoe4879a22014-08-20 15:47:36 -0700104 const chromeos::data_encoding::WebParamList& params = {}) const;
Alex Vakulenko3cb466c2014-04-15 11:36:32 -0700105
106 // Similar to GetServiceURL, GetOAuthURL() returns a URL of OAuth 2.0 server.
107 // The base URL used is https://accounts.google.com/o/oauth2/.
108 std::string GetOAuthURL(
109 const std::string& subpath = {},
Alex Vakulenkoe4879a22014-08-20 15:47:36 -0700110 const chromeos::data_encoding::WebParamList& params = {}) const;
Alex Vakulenko3cb466c2014-04-15 11:36:32 -0700111
Alex Vakulenko6e3c30e2015-05-21 17:39:25 -0700112 // Starts GCD device if credentials available.
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700113 void Start();
Alex Vakulenko3cb466c2014-04-15 11:36:32 -0700114
Nathan Bullock5e022a32015-04-08 15:13:07 -0400115 // Checks whether we have credentials generated during registration.
Alex Vakulenko3fa42ae2015-06-23 15:12:22 -0700116 bool HaveRegistrationCredentials() const;
117 // Calls HaveRegistrationCredentials() and logs an error if no credentials
118 // are available.
119 bool VerifyRegistrationCredentials(chromeos::ErrorPtr* error) const;
Alex Vakulenko3cb466c2014-04-15 11:36:32 -0700120
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700121 // Gets the full device description JSON object asynchronously.
122 // Passes the device info as the first argument to |callback|, or nullptr if
123 // the device is not registered or in case of communication failure.
124 void GetDeviceInfo(const CloudRequestCallback& success_callback,
125 const CloudRequestErrorCallback& error_callback);
Alex Vakulenko3cb466c2014-04-15 11:36:32 -0700126
Anton Muhinbeb1c5b2014-10-16 18:59:57 +0400127 // Registers the device.
Anton Muhinbeb1c5b2014-10-16 18:59:57 +0400128 // Returns a device ID on success.
Vitaly Bukacad2e332015-05-14 23:33:32 -0700129 std::string RegisterDevice(const std::string& ticket_id,
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700130 chromeos::ErrorPtr* error);
Alex Vakulenko3cb466c2014-04-15 11:36:32 -0700131
Anton Muhin59755522014-11-05 21:30:12 +0400132 // Updates a command.
Anton Muhin59755522014-11-05 21:30:12 +0400133 void UpdateCommand(const std::string& command_id,
Alex Vakulenkob211c102015-04-21 11:43:23 -0700134 const base::DictionaryValue& command_patch,
135 const base::Closure& on_success,
136 const base::Closure& on_error);
Anton Muhin59755522014-11-05 21:30:12 +0400137
Vitaly Bukafa947062015-04-17 00:41:31 -0700138 // Updates basic device information.
139 bool UpdateDeviceInfo(const std::string& name,
140 const std::string& description,
141 const std::string& location,
142 chromeos::ErrorPtr* error);
143
Vitaly Buka2f7efdb2015-05-27 16:00:21 -0700144 // Updates base device config.
145 bool UpdateBaseConfig(const std::string& anonymous_access_role,
146 bool local_discovery_enabled,
147 bool local_pairing_enabled,
148 chromeos::ErrorPtr* error);
149
Vitaly Bukaff81db62015-05-14 21:25:45 -0700150 // Updates GCD service configuration. Usually for testing.
151 bool UpdateServiceConfig(const std::string& client_id,
152 const std::string& client_secret,
153 const std::string& api_key,
154 const std::string& oauth_url,
155 const std::string& service_url,
156 chromeos::ErrorPtr* error);
157
Vitaly Bukab56872f2015-06-21 18:39:34 -0700158 // TODO(vitalybuka): remove getters and pass config to dependent code.
Vitaly Buka72410b22015-05-13 13:48:59 -0700159 const BuffetConfig& GetConfig() const { return *config_; }
Vitaly Bukab56872f2015-06-21 18:39:34 -0700160 BuffetConfig* GetMutableConfig() { return config_.get(); }
Vitaly Buka72410b22015-05-13 13:48:59 -0700161
162 base::WeakPtr<DeviceRegistrationInfo> AsWeakPtr() {
163 return weak_factory_.GetWeakPtr();
164 }
165
Alex Vakulenko3cb466c2014-04-15 11:36:32 -0700166 private:
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700167 friend class DeviceRegistrationInfoTest;
168
Christopher Wileyba983c82015-03-05 16:32:23 -0800169 // Cause DeviceRegistrationInfo to attempt to StartDevice on its own later.
170 void ScheduleStartDevice(const base::TimeDelta& later);
171
172 // Starts device execution.
173 // Device will do required start up chores and then start to listen
174 // to new commands.
175 // TODO(antonm): Consider moving into some other class.
176 void StartDevice(chromeos::ErrorPtr* error,
177 const base::TimeDelta& retry_delay);
178
David Zeuthen390d1912015-03-03 14:54:48 -0500179 // Forcibly refreshes the access token.
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700180 void RefreshAccessToken(const base::Closure& success_callback,
181 const CloudRequestErrorCallback& error_callback);
Alex Vakulenko3cb466c2014-04-15 11:36:32 -0700182
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700183 // Success callback for RefreshAccessToken().
184 void OnRefreshAccessTokenSuccess(
185 const base::Closure& success_callback,
186 const std::shared_ptr<CloudRequestErrorCallback>& error_callback,
187 chromeos::http::RequestID id,
188 std::unique_ptr<chromeos::http::Response> response);
Alex Vakulenko6b028ae2015-05-29 09:38:59 -0700189
Nathan Bullock24d189f2015-02-26 13:09:18 -0500190 // Parse the OAuth response, and sets registration status to
191 // kInvalidCredentials if our registration is no longer valid.
192 std::unique_ptr<base::DictionaryValue> ParseOAuthResponse(
Alex Vakulenkof71cca62015-04-09 15:17:17 -0700193 chromeos::http::Response* response, chromeos::ErrorPtr* error);
Nathan Bullock24d189f2015-02-26 13:09:18 -0500194
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700195 // This attempts to open a notification channel. The channel needs to be
Nathan Bullockbea91132015-02-19 09:13:33 -0500196 // restarted anytime the access_token is refreshed.
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700197 void StartNotificationChannel();
Nathan Bullockbea91132015-02-19 09:13:33 -0500198
Anton Muhinac661ab2014-10-03 20:29:48 +0400199 // Do a HTTPS request to cloud services.
200 // Handles many cases like reauthorization, 5xx HTTP response codes
201 // and device removal. It is a recommended way to do cloud API
202 // requests.
203 // TODO(antonm): Consider moving into some other class.
204 void DoCloudRequest(
Anton Muhin233d2ee2014-10-22 15:16:24 +0400205 const std::string& method,
Anton Muhinac661ab2014-10-03 20:29:48 +0400206 const std::string& url,
207 const base::DictionaryValue* body,
Alex Vakulenko0357c032015-01-06 16:32:31 -0800208 const CloudRequestCallback& success_callback,
209 const CloudRequestErrorCallback& error_callback);
Anton Muhinac661ab2014-10-03 20:29:48 +0400210
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700211 // Helper for DoCloudRequest().
212 struct CloudRequestData {
213 std::string method;
214 std::string url;
215 std::string body;
216 CloudRequestCallback success_callback;
217 CloudRequestErrorCallback error_callback;
218 };
219 void SendCloudRequest(const std::shared_ptr<const CloudRequestData>& data);
220 void OnCloudRequestSuccess(
221 const std::shared_ptr<const CloudRequestData>& data,
222 chromeos::http::RequestID request_id,
223 std::unique_ptr<chromeos::http::Response> response);
224 void OnCloudRequestError(
225 const std::shared_ptr<const CloudRequestData>& data,
226 chromeos::http::RequestID request_id,
227 const chromeos::Error* error);
228 void RetryCloudRequest(
229 const std::shared_ptr<const CloudRequestData>& data);
230 void OnAccessTokenRefreshed(
231 const std::shared_ptr<const CloudRequestData>& data);
232 void OnAccessTokenError(
233 const std::shared_ptr<const CloudRequestData>& data,
234 const chromeos::Error* error);
235
Christopher Wileyba983c82015-03-05 16:32:23 -0800236 void UpdateDeviceResource(const base::Closure& on_success,
237 const CloudRequestErrorCallback& on_failure);
Anton Muhinc635c592014-10-28 21:48:08 +0400238
Christopher Wileyba983c82015-03-05 16:32:23 -0800239 void FetchCommands(
240 const base::Callback<void(const base::ListValue&)>& on_success,
241 const CloudRequestErrorCallback& on_failure);
Anton Muhinc635c592014-10-28 21:48:08 +0400242
Alex Vakulenkod05725f2015-05-27 15:48:19 -0700243 // Processes the command list that is fetched from the server on connection.
244 // Aborts commands which are in transitional states and publishes queued
245 // commands which are queued.
246 void ProcessInitialCommandList(const base::ListValue& commands);
Anton Muhinc635c592014-10-28 21:48:08 +0400247
Anton Muhind07e2062014-10-27 10:53:29 +0400248 void PublishCommands(const base::ListValue& commands);
Alex Vakulenko6e3c30e2015-05-21 17:39:25 -0700249 void PublishCommand(const base::DictionaryValue& command);
Anton Muhind07e2062014-10-27 10:53:29 +0400250
Anton Muhinb8315622014-11-20 03:17:05 +0400251 void PublishStateUpdates();
Alex Vakulenko93ba0bd2015-06-19 14:06:46 -0700252 void OnPublishStateSuccess(const base::DictionaryValue& reply);
253 void OnPublishStateError(const chromeos::Error* error);
Anton Muhinb8315622014-11-20 03:17:05 +0400254
Alex Vakulenkod1978d32015-04-29 17:33:26 -0700255 // If unrecoverable error occurred (e.g. error parsing command instance),
256 // notify the server that the command is aborted by the device.
257 void NotifyCommandAborted(const std::string& command_id,
258 chromeos::ErrorPtr error);
259
260 // When NotifyCommandAborted() fails, RetryNotifyCommandAborted() schedules
261 // a retry attempt.
262 void RetryNotifyCommandAborted(const std::string& command_id,
263 chromeos::ErrorPtr error);
264
Alex Vakulenko9ea5a322015-04-17 15:35:34 -0700265 // Builds Cloud API devices collection REST resource which matches
Anton Muhind8d32162014-10-02 20:37:00 +0400266 // current state of the device including command definitions
267 // for all supported commands and current device state.
268 std::unique_ptr<base::DictionaryValue> BuildDeviceResource(
269 chromeos::ErrorPtr* error);
270
Christopher Wileyc900e482015-02-15 15:42:04 -0800271 void SetRegistrationStatus(RegistrationStatus new_status);
Vitaly Buka620bd7e2015-03-16 01:07:01 -0700272 void SetDeviceId(const std::string& device_id);
Christopher Wileyc900e482015-02-15 15:42:04 -0800273
Alex Vakulenko9ea5a322015-04-17 15:35:34 -0700274 // Callback called when command definitions are changed to re-publish new CDD.
275 void OnCommandDefsChanged();
Vitaly Bukac903d282015-05-26 17:03:08 -0700276 void OnStateChanged();
Alex Vakulenko9ea5a322015-04-17 15:35:34 -0700277
Alex Vakulenko6b40d8f2015-06-24 11:44:22 -0700278 // Overrides from NotificationDelegate.
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700279 void OnConnected(const std::string& channel_name) override;
280 void OnDisconnected() override;
281 void OnPermanentFailure() override;
Alex Vakulenko6e3c30e2015-05-21 17:39:25 -0700282 void OnCommandCreated(const base::DictionaryValue& command) override;
Alex Vakulenko6b40d8f2015-06-24 11:44:22 -0700283 void OnDeviceDeleted(const std::string& device_id) override;
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700284
Alex Vakulenko3fa42ae2015-06-23 15:12:22 -0700285 // Wipes out the device registration information and stops server connections.
286 void MarkDeviceUnregistered();
287
Nathan Bullockd9e0bcd2015-02-11 11:36:39 -0500288 // Transient data
289 std::string access_token_;
290 base::Time access_token_expiration_;
Nathan Bullockd9e0bcd2015-02-11 11:36:39 -0500291
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700292 // HTTP transport used for communications.
Alex Vakulenkocca20932014-08-20 17:35:12 -0700293 std::shared_ptr<chromeos::http::Transport> transport_;
Alex Vakulenko1f30a622014-07-23 11:13:15 -0700294 // Global command manager.
295 std::shared_ptr<CommandManager> command_manager_;
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700296 // Device state manager.
Anton Muhinb8315622014-11-20 03:17:05 +0400297 std::shared_ptr<StateManager> state_manager_;
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700298
Vitaly Buka867b0882015-04-16 10:03:26 -0700299 std::unique_ptr<BuffetConfig> config_;
Anton Muhin332df192014-11-22 05:59:14 +0400300
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700301 // Backoff manager for DoCloudRequest() method.
302 std::unique_ptr<chromeos::BackoffEntry::Policy> cloud_backoff_policy_;
303 std::unique_ptr<chromeos::BackoffEntry> cloud_backoff_entry_;
304
Alex Vakulenko93ba0bd2015-06-19 14:06:46 -0700305 // Flag set to true while a device state update patch request is in flight
306 // to the cloud server.
307 bool device_state_update_pending_{false};
308
Alex Vakulenkoeedf3be2015-05-13 17:52:02 -0700309 const bool notifications_enabled_;
310 std::unique_ptr<NotificationChannel> primary_notification_channel_;
Alex Vakulenkod05725f2015-05-27 15:48:19 -0700311 std::unique_ptr<PullChannel> pull_channel_;
312 NotificationChannel* current_notification_channel_{nullptr};
Alex Vakulenko6b028ae2015-05-29 09:38:59 -0700313 bool notification_channel_starting_{false};
Christopher Wileyd732bd02015-04-07 11:11:18 -0700314
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700315 privetd::ShillClient* shill_client_{nullptr};
316
Christopher Wileyc900e482015-02-15 15:42:04 -0800317 // Tracks our current registration status.
Vitaly Bukab055f152015-03-12 13:41:43 -0700318 RegistrationStatus registration_status_{RegistrationStatus::kUnconfigured};
Christopher Wileyc900e482015-02-15 15:42:04 -0800319
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700320 std::vector<OnRegistrationChangedCallback> on_registration_changed_;
321
Christopher Wileycd419662015-02-06 17:51:43 -0800322 base::WeakPtrFactory<DeviceRegistrationInfo> weak_factory_{this};
Alex Vakulenko3cb466c2014-04-15 11:36:32 -0700323 DISALLOW_COPY_AND_ASSIGN(DeviceRegistrationInfo);
324};
325
326} // namespace buffet
327
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700328#endif // BUFFET_DEVICE_REGISTRATION_INFO_H_