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.
4
5#include "google_apis/gcm/engine/heartbeat_manager.h"
6
7#include "google_apis/gcm/protocol/mcs.pb.h"
8#include "net/base/network_change_notifier.h"
9
10namespace gcm {
11
12namespace {
13// The default heartbeat when on a mobile or unknown network .
14const int64 kCellHeartbeatDefaultMs = 1000 * 60 * 28;  // 28 minutes.
15// The default heartbeat when on WiFi (also used for ethernet).
16const int64 kWifiHeartbeatDefaultMs = 1000 * 60 * 15;  // 15 minutes.
17// The default heartbeat ack interval.
18const int64 kHeartbeatAckDefaultMs = 1000 * 60 * 1;  // 1 minute.
19}  // namespace
20
21HeartbeatManager::HeartbeatManager()
22    : waiting_for_ack_(false),
23      heartbeat_interval_ms_(0),
24      server_interval_ms_(0),
25      heartbeat_timer_(true  /* retain user task */,
26                       false  /* not repeating */),
27      weak_ptr_factory_(this) {}
28
29HeartbeatManager::~HeartbeatManager() {}
30
31void HeartbeatManager::Start(
32    const base::Closure& send_heartbeat_callback,
33    const base::Closure& trigger_reconnect_callback) {
34  DCHECK(!send_heartbeat_callback.is_null());
35  DCHECK(!trigger_reconnect_callback.is_null());
36  send_heartbeat_callback_ = send_heartbeat_callback;
37  trigger_reconnect_callback_ = trigger_reconnect_callback;
38
39  // Kicks off the timer.
40  waiting_for_ack_ = false;
41  RestartTimer();
42}
43
44void HeartbeatManager::Stop() {
45  heartbeat_timer_.Stop();
46  waiting_for_ack_ = false;
47}
48
49void HeartbeatManager::OnHeartbeatAcked() {
50  if (!heartbeat_timer_.IsRunning())
51    return;
52
53  DCHECK(!send_heartbeat_callback_.is_null());
54  DCHECK(!trigger_reconnect_callback_.is_null());
55  waiting_for_ack_ = false;
56  RestartTimer();
57}
58
59void HeartbeatManager::UpdateHeartbeatConfig(
60    const mcs_proto::HeartbeatConfig& config) {
61  if (!config.IsInitialized() ||
62      !config.has_interval_ms() ||
63      config.interval_ms() <= 0) {
64    return;
65  }
66  DVLOG(1) << "Updating heartbeat interval to " << config.interval_ms();
67  server_interval_ms_ = config.interval_ms();
68}
69
70base::TimeTicks HeartbeatManager::GetNextHeartbeatTime() const {
71  if (heartbeat_timer_.IsRunning())
72    return heartbeat_timer_.desired_run_time();
73  else
74    return base::TimeTicks();
75}
76
77void HeartbeatManager::OnHeartbeatTriggered() {
78  if (waiting_for_ack_) {
79    LOG(WARNING) << "Lost connection to MCS, reconnecting.";
80    Stop();
81    trigger_reconnect_callback_.Run();
82    return;
83  }
84
85  waiting_for_ack_ = true;
86  RestartTimer();
87  send_heartbeat_callback_.Run();
88}
89
90void HeartbeatManager::RestartTimer() {
91  if (!waiting_for_ack_) {
92    // Recalculate the timer interval based network type.
93    if (server_interval_ms_ != 0) {
94      // If a server interval is set, it overrides any local one.
95      heartbeat_interval_ms_ = server_interval_ms_;
96    } else if (net::NetworkChangeNotifier::GetConnectionType() ==
97                   net::NetworkChangeNotifier::CONNECTION_WIFI ||
98               net::NetworkChangeNotifier::GetConnectionType() ==
99                   net::NetworkChangeNotifier::CONNECTION_ETHERNET) {
100      heartbeat_interval_ms_ = kWifiHeartbeatDefaultMs;
101    } else {
102      // For unknown connections, use the longer cellular heartbeat interval.
103      heartbeat_interval_ms_ = kCellHeartbeatDefaultMs;
104    }
105    DVLOG(1) << "Sending next heartbeat in "
106             << heartbeat_interval_ms_ << " ms.";
107  } else {
108    heartbeat_interval_ms_ = kHeartbeatAckDefaultMs;
109    DVLOG(1) << "Resetting timer for ack with "
110             << heartbeat_interval_ms_ << " ms interval.";
111  }
112  heartbeat_timer_.Start(FROM_HERE,
113                         base::TimeDelta::FromMilliseconds(
114                             heartbeat_interval_ms_),
115                         base::Bind(&HeartbeatManager::OnHeartbeatTriggered,
116                                    weak_ptr_factory_.GetWeakPtr()));
117}
118
119}  // namespace gcm
120