Add ICU message format support
[chromium-blink-merge.git] / google_apis / gcm / engine / heartbeat_manager.cc
blob293f41667ebdff12ea102960606b01806959d807
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/location.h"
9 #include "base/metrics/histogram.h"
10 #include "base/power_monitor/power_monitor.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "base/time/time.h"
13 #include "base/timer/timer.h"
14 #include "google_apis/gcm/protocol/mcs.pb.h"
15 #include "net/base/network_change_notifier.h"
17 namespace gcm {
19 namespace {
20 // The default heartbeat when on a mobile or unknown network .
21 const int kCellHeartbeatDefaultMs = 1000 * 60 * 28; // 28 minutes.
22 // The default heartbeat when on WiFi (also used for ethernet).
23 const int kWifiHeartbeatDefaultMs = 1000 * 60 * 15; // 15 minutes.
24 // The default heartbeat ack interval.
25 const int kHeartbeatAckDefaultMs = 1000 * 60 * 1; // 1 minute.
26 // Minimum allowed client default heartbeat interval.
27 const int kMinClientHeartbeatIntervalMs = 1000 * 60 * 2; // 2 minutes.
29 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
30 // The period at which to check if the heartbeat time has passed. Used to
31 // protect against platforms where the timer is delayed by the system being
32 // suspended. Only needed on linux because the other OSes provide a standard
33 // way to be notified of system suspend and resume events.
34 const int kHeartbeatMissedCheckMs = 1000 * 60 * 5; // 5 minutes.
35 #endif // defined(OS_LINUX) && !defined(OS_CHROMEOS)
37 } // namespace
39 HeartbeatManager::HeartbeatManager()
40 : waiting_for_ack_(false),
41 heartbeat_interval_ms_(0),
42 server_interval_ms_(0),
43 client_interval_ms_(0),
44 heartbeat_timer_(new base::Timer(true /* retain_user_task */,
45 false /* is_repeating */)),
46 weak_ptr_factory_(this) {
47 // Listen for system suspend and resume events.
48 base::PowerMonitor* monitor = base::PowerMonitor::Get();
49 if (monitor)
50 monitor->AddObserver(this);
53 HeartbeatManager::~HeartbeatManager() {
54 // Stop listening for system suspend and resume events.
55 base::PowerMonitor* monitor = base::PowerMonitor::Get();
56 if (monitor)
57 monitor->RemoveObserver(this);
60 void HeartbeatManager::Start(
61 const base::Closure& send_heartbeat_callback,
62 const ReconnectCallback& trigger_reconnect_callback) {
63 DCHECK(!send_heartbeat_callback.is_null());
64 DCHECK(!trigger_reconnect_callback.is_null());
65 send_heartbeat_callback_ = send_heartbeat_callback;
66 trigger_reconnect_callback_ = trigger_reconnect_callback;
68 // Kicks off the timer.
69 waiting_for_ack_ = false;
70 RestartTimer();
73 void HeartbeatManager::Stop() {
74 heartbeat_expected_time_ = base::Time();
75 heartbeat_interval_ms_ = 0;
76 heartbeat_timer_->Stop();
77 waiting_for_ack_ = false;
80 void HeartbeatManager::OnHeartbeatAcked() {
81 if (!heartbeat_timer_->IsRunning())
82 return;
84 DCHECK(!send_heartbeat_callback_.is_null());
85 DCHECK(!trigger_reconnect_callback_.is_null());
86 waiting_for_ack_ = false;
87 RestartTimer();
90 void HeartbeatManager::UpdateHeartbeatConfig(
91 const mcs_proto::HeartbeatConfig& config) {
92 if (!config.IsInitialized() ||
93 !config.has_interval_ms() ||
94 config.interval_ms() <= 0) {
95 return;
97 DVLOG(1) << "Updating server heartbeat interval to " << config.interval_ms();
98 server_interval_ms_ = config.interval_ms();
101 base::TimeTicks HeartbeatManager::GetNextHeartbeatTime() const {
102 if (heartbeat_timer_->IsRunning())
103 return heartbeat_timer_->desired_run_time();
104 else
105 return base::TimeTicks();
108 void HeartbeatManager::UpdateHeartbeatTimer(scoped_ptr<base::Timer> timer) {
109 bool was_running = heartbeat_timer_->IsRunning();
110 base::TimeDelta remaining_delay =
111 heartbeat_timer_->desired_run_time() - base::TimeTicks::Now();
112 base::Closure timer_task(heartbeat_timer_->user_task());
114 heartbeat_timer_->Stop();
115 heartbeat_timer_ = timer.Pass();
117 if (was_running)
118 heartbeat_timer_->Start(FROM_HERE, remaining_delay, timer_task);
121 void HeartbeatManager::OnResume() {
122 CheckForMissedHeartbeat();
125 void HeartbeatManager::OnHeartbeatTriggered() {
126 // Reset the weak pointers used for heartbeat checks.
127 weak_ptr_factory_.InvalidateWeakPtrs();
129 if (waiting_for_ack_) {
130 LOG(WARNING) << "Lost connection to MCS, reconnecting.";
131 ResetConnection(ConnectionFactory::HEARTBEAT_FAILURE);
132 return;
135 waiting_for_ack_ = true;
136 RestartTimer();
137 send_heartbeat_callback_.Run();
140 void HeartbeatManager::RestartTimer() {
141 if (!waiting_for_ack_) {
142 // Recalculate the timer interval based network type.
143 // Server interval takes precedence over client interval, even if the latter
144 // is less.
145 if (server_interval_ms_ != 0) {
146 // If a server interval is set, it overrides any local one.
147 heartbeat_interval_ms_ = server_interval_ms_;
148 } else if (HasClientHeartbeatInterval()) {
149 // Client interval might have been adjusted up, which should only take
150 // effect during a reconnection.
151 if (client_interval_ms_ < heartbeat_interval_ms_ ||
152 heartbeat_interval_ms_ == 0) {
153 heartbeat_interval_ms_ = client_interval_ms_;
155 } else {
156 heartbeat_interval_ms_ = GetDefaultHeartbeatInterval();
158 DVLOG(1) << "Sending next heartbeat in "
159 << heartbeat_interval_ms_ << " ms.";
160 } else {
161 heartbeat_interval_ms_ = kHeartbeatAckDefaultMs;
162 DVLOG(1) << "Resetting timer for ack with "
163 << heartbeat_interval_ms_ << " ms interval.";
166 heartbeat_expected_time_ =
167 base::Time::Now() +
168 base::TimeDelta::FromMilliseconds(heartbeat_interval_ms_);
169 heartbeat_timer_->Start(FROM_HERE,
170 base::TimeDelta::FromMilliseconds(
171 heartbeat_interval_ms_),
172 base::Bind(&HeartbeatManager::OnHeartbeatTriggered,
173 weak_ptr_factory_.GetWeakPtr()));
175 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
176 // Windows, Mac, Android, iOS, and Chrome OS all provide a way to be notified
177 // when the system is suspending or resuming. The only one that does not is
178 // Linux so we need to poll to check for missed heartbeats.
179 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
180 FROM_HERE,
181 base::Bind(&HeartbeatManager::CheckForMissedHeartbeat,
182 weak_ptr_factory_.GetWeakPtr()),
183 base::TimeDelta::FromMilliseconds(kHeartbeatMissedCheckMs));
184 #endif // defined(OS_LINUX) && !defined(OS_CHROMEOS)
187 void HeartbeatManager::CheckForMissedHeartbeat() {
188 // If there's no heartbeat pending, return without doing anything.
189 if (heartbeat_expected_time_.is_null())
190 return;
192 // If the heartbeat has been missed, manually trigger it.
193 if (base::Time::Now() > heartbeat_expected_time_) {
194 UMA_HISTOGRAM_LONG_TIMES("GCM.HeartbeatMissedDelta",
195 base::Time::Now() - heartbeat_expected_time_);
196 OnHeartbeatTriggered();
197 return;
200 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
201 // Otherwise check again later.
202 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
203 FROM_HERE,
204 base::Bind(&HeartbeatManager::CheckForMissedHeartbeat,
205 weak_ptr_factory_.GetWeakPtr()),
206 base::TimeDelta::FromMilliseconds(kHeartbeatMissedCheckMs));
207 #endif // defined(OS_LINUX) && !defined(OS_CHROMEOS)
210 int HeartbeatManager::GetDefaultHeartbeatInterval() {
211 // For unknown connections, use the longer cellular heartbeat interval.
212 int heartbeat_interval_ms = kCellHeartbeatDefaultMs;
213 if (net::NetworkChangeNotifier::GetConnectionType() ==
214 net::NetworkChangeNotifier::CONNECTION_WIFI ||
215 net::NetworkChangeNotifier::GetConnectionType() ==
216 net::NetworkChangeNotifier::CONNECTION_ETHERNET) {
217 heartbeat_interval_ms = kWifiHeartbeatDefaultMs;
219 return heartbeat_interval_ms;
222 int HeartbeatManager::GetMaxClientHeartbeatIntervalMs() {
223 return GetDefaultHeartbeatInterval();
226 int HeartbeatManager::GetMinClientHeartbeatIntervalMs() {
227 // Returning a constant. This should be adjusted for connection type, like the
228 // default/max interval.
229 return kMinClientHeartbeatIntervalMs;
232 void HeartbeatManager::SetClientHeartbeatIntervalMs(int interval_ms) {
233 if ((interval_ms != 0 && !IsValidClientHeartbeatInterval(interval_ms)) ||
234 interval_ms == client_interval_ms_) {
235 return;
238 client_interval_ms_ = interval_ms;
239 // Only reset connection if the new heartbeat interval is shorter. If it is
240 // longer, the connection will reset itself at some point and interval will be
241 // fixed.
242 if (client_interval_ms_ > 0 && client_interval_ms_ < heartbeat_interval_ms_) {
243 ResetConnection(ConnectionFactory::NEW_HEARTBEAT_INTERVAL);
247 int HeartbeatManager::GetClientHeartbeatIntervalMs() {
248 return client_interval_ms_;
251 bool HeartbeatManager::HasClientHeartbeatInterval() {
252 return client_interval_ms_ != 0;
255 bool HeartbeatManager::IsValidClientHeartbeatInterval(int interval) {
256 int max_heartbeat_interval = GetDefaultHeartbeatInterval();
257 return kMinClientHeartbeatIntervalMs <= interval &&
258 interval <= max_heartbeat_interval;
261 void HeartbeatManager::ResetConnection(
262 ConnectionFactory::ConnectionResetReason reason) {
263 Stop();
264 trigger_reconnect_callback_.Run(reason);
267 } // namespace gcm