1 // Copyright 2014 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 "google_apis/gcm/engine/heartbeat_manager.h"
7 #include "base/callback.h"
8 #include "base/metrics/histogram.h"
9 #include "base/power_monitor/power_monitor.h"
10 #include "base/time/time.h"
11 #include "base/timer/timer.h"
12 #include "google_apis/gcm/protocol/mcs.pb.h"
13 #include "net/base/network_change_notifier.h"
18 // The default heartbeat when on a mobile or unknown network .
19 const int64 kCellHeartbeatDefaultMs
= 1000 * 60 * 28; // 28 minutes.
20 // The default heartbeat when on WiFi (also used for ethernet).
21 const int64 kWifiHeartbeatDefaultMs
= 1000 * 60 * 15; // 15 minutes.
22 // The default heartbeat ack interval.
23 const int64 kHeartbeatAckDefaultMs
= 1000 * 60 * 1; // 1 minute.
25 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
26 // The period at which to check if the heartbeat time has passed. Used to
27 // protect against platforms where the timer is delayed by the system being
28 // suspended. Only needed on linux because the other OSes provide a standard
29 // way to be notified of system suspend and resume events.
30 const int kHeartbeatMissedCheckMs
= 1000 * 60 * 5; // 5 minutes.
31 #endif // defined(OS_LINUX) && !defined(OS_CHROMEOS)
35 HeartbeatManager::HeartbeatManager()
36 : waiting_for_ack_(false),
37 heartbeat_interval_ms_(0),
38 server_interval_ms_(0),
39 heartbeat_timer_(new base::Timer(true /* retain_user_task */,
40 false /* is_repeating */)),
41 weak_ptr_factory_(this) {
42 // Listen for system suspend and resume events.
43 base::PowerMonitor
* monitor
= base::PowerMonitor::Get();
45 monitor
->AddObserver(this);
48 HeartbeatManager::~HeartbeatManager() {
49 // Stop listening for system suspend and resume events.
50 base::PowerMonitor
* monitor
= base::PowerMonitor::Get();
52 monitor
->RemoveObserver(this);
55 void HeartbeatManager::Start(
56 const base::Closure
& send_heartbeat_callback
,
57 const base::Closure
& trigger_reconnect_callback
) {
58 DCHECK(!send_heartbeat_callback
.is_null());
59 DCHECK(!trigger_reconnect_callback
.is_null());
60 send_heartbeat_callback_
= send_heartbeat_callback
;
61 trigger_reconnect_callback_
= trigger_reconnect_callback
;
63 // Kicks off the timer.
64 waiting_for_ack_
= false;
68 void HeartbeatManager::Stop() {
69 heartbeat_expected_time_
= base::Time();
70 heartbeat_timer_
->Stop();
71 waiting_for_ack_
= false;
74 void HeartbeatManager::OnHeartbeatAcked() {
75 if (!heartbeat_timer_
->IsRunning())
78 DCHECK(!send_heartbeat_callback_
.is_null());
79 DCHECK(!trigger_reconnect_callback_
.is_null());
80 waiting_for_ack_
= false;
84 void HeartbeatManager::UpdateHeartbeatConfig(
85 const mcs_proto::HeartbeatConfig
& config
) {
86 if (!config
.IsInitialized() ||
87 !config
.has_interval_ms() ||
88 config
.interval_ms() <= 0) {
91 DVLOG(1) << "Updating heartbeat interval to " << config
.interval_ms();
92 server_interval_ms_
= config
.interval_ms();
95 base::TimeTicks
HeartbeatManager::GetNextHeartbeatTime() const {
96 if (heartbeat_timer_
->IsRunning())
97 return heartbeat_timer_
->desired_run_time();
99 return base::TimeTicks();
102 void HeartbeatManager::UpdateHeartbeatTimer(scoped_ptr
<base::Timer
> timer
) {
103 bool was_running
= heartbeat_timer_
->IsRunning();
104 base::TimeDelta remaining_delay
=
105 heartbeat_timer_
->desired_run_time() - base::TimeTicks::Now();
106 base::Closure
timer_task(heartbeat_timer_
->user_task());
108 heartbeat_timer_
->Stop();
109 heartbeat_timer_
= timer
.Pass();
112 heartbeat_timer_
->Start(FROM_HERE
, remaining_delay
, timer_task
);
115 void HeartbeatManager::OnResume() {
116 CheckForMissedHeartbeat();
119 void HeartbeatManager::OnHeartbeatTriggered() {
120 // Reset the weak pointers used for heartbeat checks.
121 weak_ptr_factory_
.InvalidateWeakPtrs();
123 if (waiting_for_ack_
) {
124 LOG(WARNING
) << "Lost connection to MCS, reconnecting.";
126 trigger_reconnect_callback_
.Run();
130 waiting_for_ack_
= true;
132 send_heartbeat_callback_
.Run();
135 void HeartbeatManager::RestartTimer() {
136 if (!waiting_for_ack_
) {
137 // Recalculate the timer interval based network type.
138 if (server_interval_ms_
!= 0) {
139 // If a server interval is set, it overrides any local one.
140 heartbeat_interval_ms_
= server_interval_ms_
;
141 } else if (net::NetworkChangeNotifier::GetConnectionType() ==
142 net::NetworkChangeNotifier::CONNECTION_WIFI
||
143 net::NetworkChangeNotifier::GetConnectionType() ==
144 net::NetworkChangeNotifier::CONNECTION_ETHERNET
) {
145 heartbeat_interval_ms_
= kWifiHeartbeatDefaultMs
;
147 // For unknown connections, use the longer cellular heartbeat interval.
148 heartbeat_interval_ms_
= kCellHeartbeatDefaultMs
;
150 DVLOG(1) << "Sending next heartbeat in "
151 << heartbeat_interval_ms_
<< " ms.";
153 heartbeat_interval_ms_
= kHeartbeatAckDefaultMs
;
154 DVLOG(1) << "Resetting timer for ack with "
155 << heartbeat_interval_ms_
<< " ms interval.";
158 heartbeat_expected_time_
=
160 base::TimeDelta::FromMilliseconds(heartbeat_interval_ms_
);
161 heartbeat_timer_
->Start(FROM_HERE
,
162 base::TimeDelta::FromMilliseconds(
163 heartbeat_interval_ms_
),
164 base::Bind(&HeartbeatManager::OnHeartbeatTriggered
,
165 weak_ptr_factory_
.GetWeakPtr()));
167 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
168 // Windows, Mac, Android, iOS, and Chrome OS all provide a way to be notified
169 // when the system is suspending or resuming. The only one that does not is
170 // Linux so we need to poll to check for missed heartbeats.
171 base::MessageLoop::current()->PostDelayedTask(
173 base::Bind(&HeartbeatManager::CheckForMissedHeartbeat
,
174 weak_ptr_factory_
.GetWeakPtr()),
175 base::TimeDelta::FromMilliseconds(kHeartbeatMissedCheckMs
));
176 #endif // defined(OS_LINUX) && !defined(OS_CHROMEOS)
179 void HeartbeatManager::CheckForMissedHeartbeat() {
180 // If there's no heartbeat pending, return without doing anything.
181 if (heartbeat_expected_time_
.is_null())
184 // If the heartbeat has been missed, manually trigger it.
185 if (base::Time::Now() > heartbeat_expected_time_
) {
186 UMA_HISTOGRAM_LONG_TIMES("GCM.HeartbeatMissedDelta",
187 base::Time::Now() - heartbeat_expected_time_
);
188 OnHeartbeatTriggered();
192 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
193 // Otherwise check again later.
194 base::MessageLoop::current()->PostDelayedTask(
196 base::Bind(&HeartbeatManager::CheckForMissedHeartbeat
,
197 weak_ptr_factory_
.GetWeakPtr()),
198 base::TimeDelta::FromMilliseconds(kHeartbeatMissedCheckMs
));
199 #endif // defined(OS_LINUX) && !defined(OS_CHROMEOS)