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