1 // Copyright 2013 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 "cloud_print/gcp20/prototype/cloud_print_xmpp_listener.h"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/rand_util.h"
11 #include "base/single_thread_task_runner.h"
12 #include "cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.h"
13 #include "jingle/notifier/base/notifier_options.h"
14 #include "jingle/notifier/listener/push_client.h"
18 const int kUrgentPingInterval = 60; // in seconds
19 const int kPingTimeout = 30; // in seconds
20 const double kRandomDelayPercentage = 0.2;
22 const char kCloudPrintPushNotificationsSource[] = "cloudprint.google.com";
24 // Splits message into two parts (e.g. '<device_id>/delete' will be splitted
25 // into '<device_id>' and '/delete').
26 void TokenizeXmppMessage(const std::string& message, std::string* device_id,
28 std::string::size_type pos = message.find('/');
29 if (pos != std::string::npos) {
30 *device_id = message.substr(0, pos);
31 *path = message.substr(pos);
40 CloudPrintXmppListener::CloudPrintXmppListener(
41 const std::string& robot_email,
42 int standard_ping_interval,
43 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
45 : robot_email_(robot_email),
46 standard_ping_interval_(standard_ping_interval),
47 ping_timeouts_posted_(0),
48 ping_responses_pending_(0),
49 ping_scheduled_(false),
50 context_getter_(new CloudPrintURLRequestContextGetter(task_runner)),
54 CloudPrintXmppListener::~CloudPrintXmppListener() {
56 push_client_->RemoveObserver(this);
61 void CloudPrintXmppListener::Connect(const std::string& access_token) {
62 access_token_ = access_token;
63 ping_responses_pending_ = 0;
64 ping_scheduled_ = false;
66 notifier::NotifierOptions options;
67 options.request_context_getter = context_getter_;
68 options.auth_mechanism = "X-OAUTH2";
69 options.try_ssltcp_first = true;
70 push_client_ = notifier::PushClient::CreateDefault(options);
72 notifier::Subscription subscription;
73 subscription.channel = kCloudPrintPushNotificationsSource;
74 subscription.from = kCloudPrintPushNotificationsSource;
76 notifier::SubscriptionList list;
77 list.push_back(subscription);
79 push_client_->UpdateSubscriptions(list);
80 push_client_->AddObserver(this);
81 push_client_->UpdateCredentials(robot_email_, access_token_);
84 void CloudPrintXmppListener::set_ping_interval(int interval) {
85 standard_ping_interval_ = interval;
88 void CloudPrintXmppListener::OnNotificationsEnabled() {
89 delegate_->OnXmppConnected();
93 void CloudPrintXmppListener::OnNotificationsDisabled(
94 notifier::NotificationsDisabledReason reason) {
96 case notifier::NOTIFICATION_CREDENTIALS_REJECTED:
97 delegate_->OnXmppAuthError();
99 case notifier::TRANSIENT_NOTIFICATION_ERROR:
103 NOTREACHED() << "XMPP failed with unexpected reason code: " << reason;
107 void CloudPrintXmppListener::OnIncomingNotification(
108 const notifier::Notification& notification) {
109 std::string device_id;
111 TokenizeXmppMessage(notification.data, &device_id, &path);
113 if (path.empty() || path == "/") {
114 delegate_->OnXmppNewPrintJob(device_id);
115 } else if (path == "/update_settings") {
116 delegate_->OnXmppNewLocalSettings(device_id);
117 } else if (path == "/delete") {
118 delegate_->OnXmppDeleteNotification(device_id);
120 LOG(ERROR) << "Cannot parse XMPP notification: " << notification.data;
124 void CloudPrintXmppListener::OnPingResponse() {
125 ping_responses_pending_ = 0;
129 void CloudPrintXmppListener::Disconnect() {
130 push_client_.reset();
131 delegate_->OnXmppNetworkError();
134 void CloudPrintXmppListener::SchedulePing() {
138 DCHECK_LE(kPingTimeout, kUrgentPingInterval);
139 int delay = (ping_responses_pending_ > 0)
140 ? kUrgentPingInterval - kPingTimeout
141 : standard_ping_interval_;
143 delay += base::RandInt(0, delay*kRandomDelayPercentage);
145 base::MessageLoop::current()->PostDelayedTask(
147 base::Bind(&CloudPrintXmppListener::SendPing, AsWeakPtr()),
148 base::TimeDelta::FromSeconds(delay));
150 ping_scheduled_ = true;
153 void CloudPrintXmppListener::SendPing() {
154 ping_scheduled_ = false;
156 DCHECK(push_client_);
157 push_client_->SendPing();
158 ++ping_responses_pending_;
160 base::MessageLoop::current()->PostDelayedTask(
162 base::Bind(&CloudPrintXmppListener::OnPingTimeoutReached, AsWeakPtr()),
163 base::TimeDelta::FromSeconds(kPingTimeout));
164 ++ping_timeouts_posted_;
167 void CloudPrintXmppListener::OnPingTimeoutReached() {
168 DCHECK_GT(ping_timeouts_posted_, 0);
169 --ping_timeouts_posted_;
170 if (ping_timeouts_posted_ > 0)
171 return; // Fake (old) timeout.
173 switch (ping_responses_pending_) {