OLD | NEW |
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/chromeos/policy/heartbeat_scheduler.h" | 5 #include "chrome/browser/chromeos/policy/heartbeat_scheduler.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
12 #include "base/location.h" | 12 #include "base/location.h" |
13 #include "base/sequenced_task_runner.h" | 13 #include "base/sequenced_task_runner.h" |
14 #include "base/strings/string_number_conversions.h" | 14 #include "base/strings/string_number_conversions.h" |
15 #include "base/time/time.h" | 15 #include "base/time/time.h" |
16 #include "chrome/common/chrome_switches.h" | 16 #include "chrome/common/chrome_switches.h" |
17 #include "components/gcm_driver/gcm_driver.h" | 17 #include "components/gcm_driver/gcm_driver.h" |
18 | 18 |
19 namespace { | 19 namespace { |
20 const int kMinHeartbeatIntervalMs = 30 * 1000; // 30 seconds | 20 const int kMinHeartbeatIntervalMs = 30 * 1000; // 30 seconds |
21 const int kMaxHeartbeatIntervalMs = 24 * 60 * 60 * 1000; // 24 hours | 21 const int kMaxHeartbeatIntervalMs = 24 * 60 * 60 * 1000; // 24 hours |
22 | 22 |
23 // Our sender ID we send up with all of our GCM messages. | 23 // Our sender ID we send up with all of our GCM messages. |
24 const char* kHeartbeatGCMAppID = "com.google.chromeos.monitoring"; | 24 const char* kHeartbeatGCMAppID = "com.google.chromeos.monitoring"; |
25 | 25 |
26 // The default destination we send our GCM messages to. | 26 // The default destination we send our GCM messages to. |
27 const char* kHeartbeatGCMDestinationID = "1013309121859"; | 27 const char* kHeartbeatGCMDestinationID = "1013309121859"; |
28 const char* kHeartbeatGCMSenderSuffix = "@google.com"; | 28 const char* kHeartbeatGCMSenderSuffix = "@google.com"; |
29 | 29 |
30 const char* kMonitoringMessageTypeKey = "type"; | 30 // Destination of upstream notification sign up message. |
| 31 const char* kUpstreamNotificationSignUpDestinationID = |
| 32 "https://gcm.googleapis.com/gcm/gcm.event_tracker"; |
| 33 |
| 34 // A bit mask, listening events of upstream notification. |
| 35 const char* kUpstreamNotificationSignUpListeningEvents = |
| 36 "7"; // START | DISCONNECTED | HEARTBEAT |
| 37 |
| 38 const char* kGcmMessageTypeKey = "type"; |
31 const char* kHeartbeatTimestampKey = "timestamp"; | 39 const char* kHeartbeatTimestampKey = "timestamp"; |
32 const char* kHeartbeatDomainNameKey = "domain_name"; | 40 const char* kHeartbeatDomainNameKey = "domain_name"; |
33 const char* kHeartbeatDeviceIDKey = "device_id"; | 41 const char* kHeartbeatDeviceIDKey = "device_id"; |
34 const char* kHeartbeatTypeValue = "hb"; | 42 const char* kHeartbeatTypeValue = "hb"; |
| 43 const char* kUpstreamNotificationNotifyKey = "notify"; |
35 | 44 |
36 // If we get an error registering with GCM, try again in two minutes. | 45 // If we get an error registering with GCM, try again in two minutes. |
37 const int64 kRegistrationRetryDelayMs = 2 * 60 * 1000; | 46 const int64 kRegistrationRetryDelayMs = 2 * 60 * 1000; |
38 | 47 |
| 48 const char* kHeartbeatSchedulerScope = |
| 49 "policy.heartbeat_scheduler.upstream_notification"; |
| 50 |
39 // Returns the destination ID for GCM heartbeats. | 51 // Returns the destination ID for GCM heartbeats. |
40 std::string GetDestinationID() { | 52 std::string GetDestinationID() { |
41 std::string receiver_id = kHeartbeatGCMDestinationID; | 53 std::string receiver_id = kHeartbeatGCMDestinationID; |
42 if (base::CommandLine::ForCurrentProcess()->HasSwitch( | 54 if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
43 switches::kMonitoringDestinationID)) { | 55 switches::kMonitoringDestinationID)) { |
44 receiver_id = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | 56 receiver_id = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
45 switches::kMonitoringDestinationID); | 57 switches::kMonitoringDestinationID); |
46 } | 58 } |
47 return receiver_id; | 59 return receiver_id; |
48 } | 60 } |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
205 if (chromeos::CrosSettingsProvider::TRUSTED != settings->PrepareTrustedValues( | 217 if (chromeos::CrosSettingsProvider::TRUSTED != settings->PrepareTrustedValues( |
206 base::Bind(&HeartbeatScheduler::RefreshHeartbeatSettings, | 218 base::Bind(&HeartbeatScheduler::RefreshHeartbeatSettings, |
207 weak_factory_.GetWeakPtr()))) { | 219 weak_factory_.GetWeakPtr()))) { |
208 return; | 220 return; |
209 } | 221 } |
210 | 222 |
211 // CrosSettings are trusted - update our cached settings (we cache the | 223 // CrosSettings are trusted - update our cached settings (we cache the |
212 // value because CrosSettings can become untrusted at arbitrary times and we | 224 // value because CrosSettings can become untrusted at arbitrary times and we |
213 // want to use the last trusted value). | 225 // want to use the last trusted value). |
214 int frequency; | 226 int frequency; |
215 if (settings->GetInteger(chromeos::kHeartbeatFrequency, &frequency)) | 227 if (settings->GetInteger(chromeos::kHeartbeatFrequency, &frequency)) { |
216 heartbeat_interval_ = EnsureValidHeartbeatInterval( | 228 heartbeat_interval_ = EnsureValidHeartbeatInterval( |
217 base::TimeDelta::FromMilliseconds(frequency)); | 229 base::TimeDelta::FromMilliseconds(frequency)); |
| 230 } |
| 231 |
| 232 gcm_driver_->AddHeartbeatInterval(kHeartbeatSchedulerScope, |
| 233 heartbeat_interval_.InMilliseconds()); |
218 | 234 |
219 bool enabled; | 235 bool enabled; |
220 if (settings->GetBoolean(chromeos::kHeartbeatEnabled, &enabled)) | 236 if (settings->GetBoolean(chromeos::kHeartbeatEnabled, &enabled)) |
221 heartbeat_enabled_ = enabled; | 237 heartbeat_enabled_ = enabled; |
222 | 238 |
223 if (!heartbeat_enabled_) { | 239 if (!heartbeat_enabled_) { |
224 // Heartbeats are no longer enabled - cancel our callback and any | 240 // Heartbeats are no longer enabled - cancel our callback and any |
225 // outstanding registration attempts and disconnect from GCM so the | 241 // outstanding registration attempts and disconnect from GCM so the |
226 // connection can be shut down. If heartbeats are re-enabled later, we | 242 // connection can be shut down. If heartbeats are re-enabled later, we |
227 // will re-register with GCM. | 243 // will re-register with GCM. |
228 heartbeat_callback_.Cancel(); | 244 heartbeat_callback_.Cancel(); |
229 ShutdownGCM(); | 245 ShutdownGCM(); |
230 } else { | 246 } else { |
231 // Schedule a new upload with the new frequency. | 247 // Schedule a new upload with the new frequency. |
232 ScheduleNextHeartbeat(); | 248 ScheduleNextHeartbeat(); |
233 } | 249 } |
234 | 250 |
235 DVLOG(1) << "heartbeat enabled: " << heartbeat_enabled_; | 251 DVLOG(1) << "heartbeat enabled: " << heartbeat_enabled_; |
236 DVLOG(1) << "heartbeat frequency: " << heartbeat_interval_; | 252 DVLOG(1) << "heartbeat frequency: " << heartbeat_interval_; |
237 } | 253 } |
238 | 254 |
239 void HeartbeatScheduler::ShutdownGCM() { | 255 void HeartbeatScheduler::ShutdownGCM() { |
240 registration_helper_.reset(); | 256 registration_helper_.reset(); |
241 registration_id_.clear(); | 257 registration_id_.clear(); |
242 if (registered_app_handler_) { | 258 if (registered_app_handler_) { |
243 registered_app_handler_ = false; | 259 registered_app_handler_ = false; |
| 260 gcm_driver_->RemoveHeartbeatInterval(kHeartbeatSchedulerScope); |
244 gcm_driver_->RemoveAppHandler(kHeartbeatGCMAppID); | 261 gcm_driver_->RemoveAppHandler(kHeartbeatGCMAppID); |
| 262 gcm_driver_->RemoveConnectionObserver(this); |
245 } | 263 } |
246 } | 264 } |
247 | 265 |
248 base::TimeDelta HeartbeatScheduler::EnsureValidHeartbeatInterval( | 266 base::TimeDelta HeartbeatScheduler::EnsureValidHeartbeatInterval( |
249 const base::TimeDelta& interval) { | 267 const base::TimeDelta& interval) { |
250 const base::TimeDelta min = base::TimeDelta::FromMilliseconds( | 268 const base::TimeDelta min = base::TimeDelta::FromMilliseconds( |
251 kMinHeartbeatIntervalMs); | 269 kMinHeartbeatIntervalMs); |
252 const base::TimeDelta max = base::TimeDelta::FromMilliseconds( | 270 const base::TimeDelta max = base::TimeDelta::FromMilliseconds( |
253 kMaxHeartbeatIntervalMs); | 271 kMaxHeartbeatIntervalMs); |
254 if (interval < min) { | 272 if (interval < min) { |
(...skipping 12 matching lines...) Expand all Loading... |
267 if (!heartbeat_enabled_) | 285 if (!heartbeat_enabled_) |
268 return; | 286 return; |
269 | 287 |
270 if (registration_id_.empty()) { | 288 if (registration_id_.empty()) { |
271 // We are not registered with the GCM service yet, so kick off registration. | 289 // We are not registered with the GCM service yet, so kick off registration. |
272 if (!registration_helper_) { | 290 if (!registration_helper_) { |
273 // Add ourselves as an AppHandler - this is required in order to setup | 291 // Add ourselves as an AppHandler - this is required in order to setup |
274 // a GCM connection. | 292 // a GCM connection. |
275 registered_app_handler_ = true; | 293 registered_app_handler_ = true; |
276 gcm_driver_->AddAppHandler(kHeartbeatGCMAppID, this); | 294 gcm_driver_->AddAppHandler(kHeartbeatGCMAppID, this); |
| 295 gcm_driver_->AddConnectionObserver(this); |
277 registration_helper_.reset(new HeartbeatRegistrationHelper( | 296 registration_helper_.reset(new HeartbeatRegistrationHelper( |
278 gcm_driver_, task_runner_)); | 297 gcm_driver_, task_runner_)); |
279 registration_helper_->Register( | 298 registration_helper_->Register( |
280 base::Bind(&HeartbeatScheduler::OnRegistrationComplete, | 299 base::Bind(&HeartbeatScheduler::OnRegistrationComplete, |
281 weak_factory_.GetWeakPtr())); | 300 weak_factory_.GetWeakPtr())); |
282 } | 301 } |
283 return; | 302 return; |
284 } | 303 } |
285 | 304 |
286 // Calculate when to fire off the next update (if it should have already | 305 // Calculate when to fire off the next update (if it should have already |
(...skipping 14 matching lines...) Expand all Loading... |
301 registration_helper_.reset(); | 320 registration_helper_.reset(); |
302 registration_id_ = registration_id; | 321 registration_id_ = registration_id; |
303 | 322 |
304 if (cloud_policy_client_) { | 323 if (cloud_policy_client_) { |
305 // TODO(binjin): Avoid sending the same GCM id to the server. | 324 // TODO(binjin): Avoid sending the same GCM id to the server. |
306 // See http://crbug.com/516375 | 325 // See http://crbug.com/516375 |
307 cloud_policy_client_->UpdateGcmId( | 326 cloud_policy_client_->UpdateGcmId( |
308 registration_id, | 327 registration_id, |
309 base::Bind(&HeartbeatScheduler::OnGcmIdUpdateRequestSent, | 328 base::Bind(&HeartbeatScheduler::OnGcmIdUpdateRequestSent, |
310 weak_factory_.GetWeakPtr())); | 329 weak_factory_.GetWeakPtr())); |
| 330 SignUpUpstreamNotification(); |
311 } | 331 } |
312 | 332 |
313 // Now that GCM registration is complete, start sending heartbeats. | 333 // Now that GCM registration is complete, start sending heartbeats. |
314 ScheduleNextHeartbeat(); | 334 ScheduleNextHeartbeat(); |
315 } | 335 } |
316 | 336 |
317 void HeartbeatScheduler::SendHeartbeat() { | 337 void HeartbeatScheduler::SendHeartbeat() { |
318 DCHECK(!registration_id_.empty()); | 338 DCHECK(!registration_id_.empty()); |
319 if (!gcm_driver_ || !heartbeat_enabled_) | 339 if (!gcm_driver_ || !heartbeat_enabled_) |
320 return; | 340 return; |
321 | 341 |
322 gcm::OutgoingMessage message; | 342 gcm::OutgoingMessage message; |
323 message.time_to_live = heartbeat_interval_.InSeconds(); | 343 message.time_to_live = heartbeat_interval_.InSeconds(); |
324 // Just use the current timestamp as the message ID - if the user changes the | 344 // Just use the current timestamp as the message ID - if the user changes the |
325 // time and we send a message with the same ID that we previously used, no | 345 // time and we send a message with the same ID that we previously used, no |
326 // big deal (the new message will replace the old, which is the behavior we | 346 // big deal (the new message will replace the old, which is the behavior we |
327 // want anyway, per: | 347 // want anyway, per: |
328 // https://developer.chrome.com/apps/cloudMessaging#send_messages | 348 // https://developer.chrome.com/apps/cloudMessaging#send_messages |
329 message.id = base::Int64ToString( | 349 message.id = base::Int64ToString( |
330 base::Time::NowFromSystemTime().ToInternalValue()); | 350 base::Time::NowFromSystemTime().ToInternalValue()); |
331 message.data[kMonitoringMessageTypeKey] = kHeartbeatTypeValue; | 351 message.data[kGcmMessageTypeKey] = kHeartbeatTypeValue; |
332 message.data[kHeartbeatTimestampKey] = base::Int64ToString( | 352 message.data[kHeartbeatTimestampKey] = base::Int64ToString( |
333 base::Time::NowFromSystemTime().ToJavaTime()); | 353 base::Time::NowFromSystemTime().ToJavaTime()); |
334 message.data[kHeartbeatDomainNameKey] = enrollment_domain_; | 354 message.data[kHeartbeatDomainNameKey] = enrollment_domain_; |
335 message.data[kHeartbeatDeviceIDKey] = device_id_; | 355 message.data[kHeartbeatDeviceIDKey] = device_id_; |
336 gcm_driver_->Send(kHeartbeatGCMAppID, | 356 gcm_driver_->Send(kHeartbeatGCMAppID, |
337 GetDestinationID() + kHeartbeatGCMSenderSuffix, | 357 GetDestinationID() + kHeartbeatGCMSenderSuffix, |
338 message, | 358 message, |
339 base::Bind(&HeartbeatScheduler::OnHeartbeatSent, | 359 base::Bind(&HeartbeatScheduler::OnHeartbeatSent, |
340 weak_factory_.GetWeakPtr())); | 360 weak_factory_.GetWeakPtr())); |
341 } | 361 } |
342 | 362 |
| 363 void HeartbeatScheduler::SignUpUpstreamNotification() { |
| 364 DCHECK(gcm_driver_); |
| 365 |
| 366 if (registration_id_.empty()) |
| 367 return; |
| 368 |
| 369 gcm::OutgoingMessage message; |
| 370 message.id = |
| 371 base::Int64ToString(base::Time::NowFromSystemTime().ToInternalValue()); |
| 372 message.data[kGcmMessageTypeKey] = kUpstreamNotificationSignUpListeningEvents; |
| 373 message.data[kUpstreamNotificationNotifyKey] = |
| 374 GetDestinationID() + kHeartbeatGCMSenderSuffix; |
| 375 gcm_driver_->Send(kHeartbeatGCMAppID, |
| 376 kUpstreamNotificationSignUpDestinationID, message, |
| 377 base::Bind(&HeartbeatScheduler::OnUpstreamNotificationSent, |
| 378 weak_factory_.GetWeakPtr())); |
| 379 } |
| 380 |
343 void HeartbeatScheduler::OnHeartbeatSent(const std::string& message_id, | 381 void HeartbeatScheduler::OnHeartbeatSent(const std::string& message_id, |
344 gcm::GCMClient::Result result) { | 382 gcm::GCMClient::Result result) { |
345 DVLOG(1) << "Monitoring heartbeat sent - result = " << result; | 383 DVLOG(1) << "Monitoring heartbeat sent - result = " << result; |
346 // Don't care if the result was successful or not - just schedule the next | 384 // Don't care if the result was successful or not - just schedule the next |
347 // heartbeat. | 385 // heartbeat. |
348 DLOG_IF(ERROR, result != gcm::GCMClient::SUCCESS) << | 386 DLOG_IF(ERROR, result != gcm::GCMClient::SUCCESS) << |
349 "Error sending monitoring heartbeat: " << result; | 387 "Error sending monitoring heartbeat: " << result; |
350 last_heartbeat_ = base::Time::NowFromSystemTime(); | 388 last_heartbeat_ = base::Time::NowFromSystemTime(); |
351 ScheduleNextHeartbeat(); | 389 ScheduleNextHeartbeat(); |
352 } | 390 } |
353 | 391 |
| 392 void HeartbeatScheduler::OnUpstreamNotificationSent( |
| 393 const std::string& message_id, |
| 394 gcm::GCMClient::Result result) { |
| 395 DVLOG(1) << "Upstream notification signup message sent - result = " << result; |
| 396 DLOG_IF(ERROR, result != gcm::GCMClient::SUCCESS) |
| 397 << "Error sending upstream notification signup message: " << result; |
| 398 } |
| 399 |
354 HeartbeatScheduler::~HeartbeatScheduler() { | 400 HeartbeatScheduler::~HeartbeatScheduler() { |
355 ShutdownGCM(); | 401 ShutdownGCM(); |
356 } | 402 } |
357 | 403 |
358 void HeartbeatScheduler::ShutdownHandler() { | 404 void HeartbeatScheduler::ShutdownHandler() { |
359 // This should never be called, because BrowserProcessImpl::StartTearDown() | 405 // This should never be called, because BrowserProcessImpl::StartTearDown() |
360 // should shutdown the BrowserPolicyConnector (which destroys this object) | 406 // should shutdown the BrowserPolicyConnector (which destroys this object) |
361 // before the GCMDriver. Our goal is to make sure that this object is always | 407 // before the GCMDriver. Our goal is to make sure that this object is always |
362 // shutdown before GCMDriver is shut down, rather than trying to handle the | 408 // shutdown before GCMDriver is shut down, rather than trying to handle the |
363 // case when GCMDriver goes away. | 409 // case when GCMDriver goes away. |
(...skipping 14 matching lines...) Expand all Loading... |
378 const std::string& app_id, | 424 const std::string& app_id, |
379 const gcm::GCMClient::SendErrorDetails& details) { | 425 const gcm::GCMClient::SendErrorDetails& details) { |
380 // Ignore send errors - we already are notified above in OnHeartbeatSent(). | 426 // Ignore send errors - we already are notified above in OnHeartbeatSent(). |
381 } | 427 } |
382 | 428 |
383 void HeartbeatScheduler::OnSendAcknowledged(const std::string& app_id, | 429 void HeartbeatScheduler::OnSendAcknowledged(const std::string& app_id, |
384 const std::string& message_id) { | 430 const std::string& message_id) { |
385 DVLOG(1) << "Heartbeat sent with message_id: " << message_id; | 431 DVLOG(1) << "Heartbeat sent with message_id: " << message_id; |
386 } | 432 } |
387 | 433 |
| 434 void HeartbeatScheduler::OnConnected(const net::IPEndPoint&) { |
| 435 SignUpUpstreamNotification(); |
| 436 } |
| 437 |
388 void HeartbeatScheduler::OnGcmIdUpdateRequestSent(bool success) { | 438 void HeartbeatScheduler::OnGcmIdUpdateRequestSent(bool success) { |
389 // TODO(binjin): Handle the failure, probably by exponential backoff. | 439 // TODO(binjin): Handle the failure, probably by exponential backoff. |
390 LOG_IF(WARNING, !success) << "Failed to send GCM id to DM server"; | 440 LOG_IF(WARNING, !success) << "Failed to send GCM id to DM server"; |
391 } | 441 } |
392 | 442 |
393 } // namespace policy | 443 } // namespace policy |
OLD | NEW |