Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / browser / chromeos / policy / heartbeat_scheduler.cc
blob74dd1e0d14689e28b40b2b9fe111ade759128268
1 // Copyright (c) 2015 The Chromium 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.
5 #include "chrome/browser/chromeos/policy/heartbeat_scheduler.h"
7 #include <string>
9 #include "base/command_line.h"
10 #include "base/location.h"
11 #include "base/sequenced_task_runner.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/time/time.h"
14 #include "chrome/common/chrome_switches.h"
15 #include "components/gcm_driver/gcm_driver.h"
17 namespace {
18 const int kMinHeartbeatIntervalMs = 30 * 1000; // 30 seconds
19 const int kMaxHeartbeatIntervalMs = 24 * 60 * 60 * 1000; // 24 hours
21 // Our sender ID we send up with all of our GCM messages.
22 const char* kHeartbeatGCMAppID = "com.google.chromeos.monitoring";
24 // The default destination we send our GCM messages to.
25 const char* kHeartbeatGCMDestinationID = "1013309121859";
26 const char* kHeartbeatGCMSenderSuffix = "@google.com";
28 const char* kMonitoringMessageTypeKey = "type";
29 const char* kHeartbeatTimestampKey = "timestamp";
30 const char* kHeartbeatDomainNameKey = "domain_name";
31 const char* kHeartbeatDeviceIDKey = "device_id";
32 const char* kHeartbeatTypeValue = "hb";
34 // If we get an error registering with GCM, try again in two minutes.
35 const int64 kRegistrationRetryDelayMs = 2 * 60 * 1000;
37 // Returns the destination ID for GCM heartbeats.
38 std::string GetDestinationID() {
39 std::string receiver_id = kHeartbeatGCMDestinationID;
40 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
41 switches::kMonitoringDestinationID)) {
42 receiver_id = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
43 switches::kMonitoringDestinationID);
45 return receiver_id;
48 } // namespace
50 namespace policy {
52 const int64 HeartbeatScheduler::kDefaultHeartbeatIntervalMs =
53 2 * 60 * 1000; // 2 minutes
55 // Helper class used to manage GCM registration (handles retrying after
56 // errors, etc).
57 class HeartbeatRegistrationHelper {
58 public:
59 typedef base::Callback<void(const std::string& registration_id)>
60 RegistrationHelperCallback;
62 HeartbeatRegistrationHelper(
63 gcm::GCMDriver* gcm_driver,
64 const scoped_refptr<base::SequencedTaskRunner>& task_runner);
66 void Register(const RegistrationHelperCallback& callback);
68 private:
69 void AttemptRegistration();
71 // Callback invoked once a registration attempt has finished.
72 void OnRegisterAttemptComplete(const std::string& registration_id,
73 gcm::GCMClient::Result result);
75 // GCMDriver to use to register.
76 gcm::GCMDriver* const gcm_driver_;
78 // Callback to invoke when we have completed GCM registration.
79 RegistrationHelperCallback callback_;
81 // TaskRunner used for scheduling retry attempts.
82 const scoped_refptr<base::SequencedTaskRunner> task_runner_;
84 // Should remain the last member so it will be destroyed first and
85 // invalidate all weak pointers.
86 base::WeakPtrFactory<HeartbeatRegistrationHelper> weak_factory_;
88 DISALLOW_COPY_AND_ASSIGN(HeartbeatRegistrationHelper);
91 HeartbeatRegistrationHelper::HeartbeatRegistrationHelper(
92 gcm::GCMDriver* gcm_driver,
93 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
94 : gcm_driver_(gcm_driver),
95 task_runner_(task_runner),
96 weak_factory_(this) {
99 void HeartbeatRegistrationHelper::Register(
100 const RegistrationHelperCallback& callback) {
101 // Should only call Register() once.
102 DCHECK(callback_.is_null());
103 callback_ = callback;
104 AttemptRegistration();
107 void HeartbeatRegistrationHelper::AttemptRegistration() {
108 std::vector<std::string> destinations;
109 destinations.push_back(GetDestinationID());
110 gcm_driver_->Register(
111 kHeartbeatGCMAppID,
112 destinations,
113 base::Bind(&HeartbeatRegistrationHelper::OnRegisterAttemptComplete,
114 weak_factory_.GetWeakPtr()));
117 void HeartbeatRegistrationHelper::OnRegisterAttemptComplete(
118 const std::string& registration_id, gcm::GCMClient::Result result) {
119 DVLOG(1) << "Received Register() response: " << result;
120 // TODO(atwilson): Track GCM errors via UMA (http://crbug.com/459238).
121 switch (result) {
122 case gcm::GCMClient::SUCCESS:
124 // Copy the callback, because the callback may free this object and
125 // we don't want to free the callback object and any bound variables
126 // until the callback exits.
127 RegistrationHelperCallback callback = callback_;
128 callback.Run(registration_id);
129 // This helper may be freed now, so do not access any member variables
130 // after this point.
131 return;
134 case gcm::GCMClient::NETWORK_ERROR:
135 case gcm::GCMClient::SERVER_ERROR:
136 // Transient error - try again after a delay.
137 task_runner_->PostDelayedTask(
138 FROM_HERE,
139 base::Bind(&HeartbeatRegistrationHelper::AttemptRegistration,
140 weak_factory_.GetWeakPtr()),
141 base::TimeDelta::FromMilliseconds(kRegistrationRetryDelayMs));
142 break;
144 case gcm::GCMClient::INVALID_PARAMETER:
145 case gcm::GCMClient::UNKNOWN_ERROR:
146 case gcm::GCMClient::GCM_DISABLED:
147 // No need to bother retrying in the case of one of these fatal errors.
148 // This means that heartbeats will remain disabled until the next
149 // restart.
150 DLOG(ERROR) << "Fatal GCM Registration error: " << result;
151 break;
153 case gcm::GCMClient::ASYNC_OPERATION_PENDING:
154 case gcm::GCMClient::TTL_EXCEEDED:
155 default:
156 NOTREACHED() << "Unexpected GCMDriver::Register() result: " << result;
157 break;
161 HeartbeatScheduler::HeartbeatScheduler(
162 gcm::GCMDriver* driver,
163 const std::string& enrollment_domain,
164 const std::string& device_id,
165 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
166 : task_runner_(task_runner),
167 enrollment_domain_(enrollment_domain),
168 device_id_(device_id),
169 heartbeat_enabled_(false),
170 heartbeat_interval_(base::TimeDelta::FromMilliseconds(
171 kDefaultHeartbeatIntervalMs)),
172 gcm_driver_(driver),
173 weak_factory_(this) {
174 // If no GCMDriver (e.g. this is loaded as part of an unrelated unit test)
175 // do nothing as no heartbeats can be sent.
176 if (!gcm_driver_)
177 return;
179 heartbeat_frequency_observer_ =
180 chromeos::CrosSettings::Get()->AddSettingsObserver(
181 chromeos::kHeartbeatFrequency,
182 base::Bind(&HeartbeatScheduler::RefreshHeartbeatSettings,
183 base::Unretained(this)));
185 heartbeat_enabled_observer_ =
186 chromeos::CrosSettings::Get()->AddSettingsObserver(
187 chromeos::kHeartbeatEnabled,
188 base::Bind(&HeartbeatScheduler::RefreshHeartbeatSettings,
189 base::Unretained(this)));
191 // Update the heartbeat frequency from settings. This will trigger a
192 // heartbeat as appropriate once the settings have been refreshed.
193 RefreshHeartbeatSettings();
196 void HeartbeatScheduler::RefreshHeartbeatSettings() {
197 // Attempt to fetch the current value of the reporting settings.
198 // If trusted values are not available, register this function to be called
199 // back when they are available.
200 chromeos::CrosSettings* settings = chromeos::CrosSettings::Get();
201 if (chromeos::CrosSettingsProvider::TRUSTED != settings->PrepareTrustedValues(
202 base::Bind(&HeartbeatScheduler::RefreshHeartbeatSettings,
203 weak_factory_.GetWeakPtr()))) {
204 return;
207 // CrosSettings are trusted - update our cached settings (we cache the
208 // value because CrosSettings can become untrusted at arbitrary times and we
209 // want to use the last trusted value).
210 int frequency;
211 if (settings->GetInteger(chromeos::kHeartbeatFrequency, &frequency))
212 heartbeat_interval_ = EnsureValidHeartbeatInterval(
213 base::TimeDelta::FromMilliseconds(frequency));
215 bool enabled;
216 if (settings->GetBoolean(chromeos::kHeartbeatEnabled, &enabled))
217 heartbeat_enabled_ = enabled;
219 if (!heartbeat_enabled_) {
220 // Heartbeats are no longer enabled - cancel our callback and any
221 // outstanding registration attempts and disconnect from GCM so the
222 // connection can be shut down. If heartbeats are re-enabled later, we
223 // will re-register with GCM.
224 heartbeat_callback_.Cancel();
225 ShutdownGCM();
226 } else {
227 // Schedule a new upload with the new frequency.
228 ScheduleNextHeartbeat();
231 DVLOG(1) << "heartbeat enabled: " << heartbeat_enabled_;
232 DVLOG(1) << "heartbeat frequency: " << heartbeat_interval_;
235 void HeartbeatScheduler::ShutdownGCM() {
236 registration_helper_.reset();
237 registration_id_.clear();
238 if (registered_app_handler_) {
239 registered_app_handler_ = false;
240 gcm_driver_->RemoveAppHandler(kHeartbeatGCMAppID);
244 base::TimeDelta HeartbeatScheduler::EnsureValidHeartbeatInterval(
245 const base::TimeDelta& interval) {
246 const base::TimeDelta min = base::TimeDelta::FromMilliseconds(
247 kMinHeartbeatIntervalMs);
248 const base::TimeDelta max = base::TimeDelta::FromMilliseconds(
249 kMaxHeartbeatIntervalMs);
250 if (interval < min) {
251 DLOG(WARNING) << "Invalid heartbeat interval: " << interval;
252 return min;
254 if (interval > max) {
255 DLOG(WARNING) << "Invalid heartbeat interval: " << interval;
256 return max;
258 return interval;
261 void HeartbeatScheduler::ScheduleNextHeartbeat() {
262 // Do nothing if heartbeats are disabled.
263 if (!heartbeat_enabled_)
264 return;
266 if (registration_id_.empty()) {
267 // We are not registered with the GCM service yet, so kick off registration.
268 if (!registration_helper_) {
269 // Add ourselves as an AppHandler - this is required in order to setup
270 // a GCM connection.
271 registered_app_handler_ = true;
272 gcm_driver_->AddAppHandler(kHeartbeatGCMAppID, this);
273 registration_helper_.reset(new HeartbeatRegistrationHelper(
274 gcm_driver_, task_runner_));
275 registration_helper_->Register(
276 base::Bind(&HeartbeatScheduler::OnRegistrationComplete,
277 weak_factory_.GetWeakPtr()));
279 return;
282 // Calculate when to fire off the next update (if it should have already
283 // happened, this yields a TimeDelta of 0).
284 base::TimeDelta delay = std::max(
285 last_heartbeat_ + heartbeat_interval_ - base::Time::NowFromSystemTime(),
286 base::TimeDelta());
288 heartbeat_callback_.Reset(base::Bind(&HeartbeatScheduler::SendHeartbeat,
289 base::Unretained(this)));
290 task_runner_->PostDelayedTask(
291 FROM_HERE, heartbeat_callback_.callback(), delay);
294 void HeartbeatScheduler::OnRegistrationComplete(
295 const std::string& registration_id) {
296 DCHECK(!registration_id.empty());
297 registration_helper_.reset();
298 registration_id_ = registration_id;
300 // Now that GCM registration is complete, start sending heartbeats.
301 ScheduleNextHeartbeat();
304 void HeartbeatScheduler::SendHeartbeat() {
305 DCHECK(!registration_id_.empty());
306 if (!gcm_driver_ || !heartbeat_enabled_)
307 return;
309 gcm::GCMClient::OutgoingMessage message;
310 message.time_to_live = heartbeat_interval_.InSeconds();
311 // Just use the current timestamp as the message ID - if the user changes the
312 // time and we send a message with the same ID that we previously used, no
313 // big deal (the new message will replace the old, which is the behavior we
314 // want anyway, per:
315 // https://developer.chrome.com/apps/cloudMessaging#send_messages
316 message.id = base::Int64ToString(
317 base::Time::NowFromSystemTime().ToInternalValue());
318 message.data[kMonitoringMessageTypeKey] = kHeartbeatTypeValue;
319 message.data[kHeartbeatTimestampKey] = base::Int64ToString(
320 base::Time::NowFromSystemTime().ToJavaTime());
321 message.data[kHeartbeatDomainNameKey] = enrollment_domain_;
322 message.data[kHeartbeatDeviceIDKey] = device_id_;
323 gcm_driver_->Send(kHeartbeatGCMAppID,
324 GetDestinationID() + kHeartbeatGCMSenderSuffix,
325 message,
326 base::Bind(&HeartbeatScheduler::OnHeartbeatSent,
327 weak_factory_.GetWeakPtr()));
330 void HeartbeatScheduler::OnHeartbeatSent(const std::string& message_id,
331 gcm::GCMClient::Result result) {
332 DVLOG(1) << "Monitoring heartbeat sent - result = " << result;
333 // Don't care if the result was successful or not - just schedule the next
334 // heartbeat.
335 DLOG_IF(ERROR, result != gcm::GCMClient::SUCCESS) <<
336 "Error sending monitoring heartbeat: " << result;
337 last_heartbeat_ = base::Time::NowFromSystemTime();
338 ScheduleNextHeartbeat();
341 HeartbeatScheduler::~HeartbeatScheduler() {
342 ShutdownGCM();
345 void HeartbeatScheduler::ShutdownHandler() {
346 // This should never be called, because BrowserProcessImpl::StartTearDown()
347 // should shutdown the BrowserPolicyConnector (which destroys this object)
348 // before the GCMDriver. Our goal is to make sure that this object is always
349 // shutdown before GCMDriver is shut down, rather than trying to handle the
350 // case when GCMDriver goes away.
351 NOTREACHED() << "HeartbeatScheduler should be destroyed before GCMDriver";
354 void HeartbeatScheduler::OnMessage(
355 const std::string& app_id,
356 const gcm::GCMClient::IncomingMessage& message) {
357 // Should never be called because we don't get any incoming messages
358 // for our app ID.
359 NOTREACHED() << "Received incoming message for " << app_id;
362 void HeartbeatScheduler::OnMessagesDeleted(const std::string& app_id) {
365 void HeartbeatScheduler::OnSendError(
366 const std::string& app_id,
367 const gcm::GCMClient::SendErrorDetails& details) {
368 // Ignore send errors - we already are notified above in OnHeartbeatSent().
371 void HeartbeatScheduler::OnSendAcknowledged(const std::string& app_id,
372 const std::string& message_id) {
373 DVLOG(1) << "Heartbeat sent with message_id: " << message_id;
376 } // namespace policy