- add sources.
[platform/framework/web/crosswalk.git] / src / cloud_print / gcp20 / prototype / cloud_print_xmpp_listener.cc
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.
4
5 #include "cloud_print/gcp20/prototype/cloud_print_xmpp_listener.h"
6
7 #include "base/bind.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"
15
16 namespace {
17
18 const int kUrgentPingInterval = 60;  // in seconds
19 const int kPingTimeout = 30;  // in seconds
20 const double kRandomDelayPercentage = 0.2;
21
22 const char kCloudPrintPushNotificationsSource[] = "cloudprint.google.com";
23
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,
27                          std::string* path) {
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);
32   } else {
33     *device_id = message;
34     path->clear();
35   }
36 }
37
38 }  // namespace
39
40 CloudPrintXmppListener::CloudPrintXmppListener(
41     const std::string& robot_email,
42     int standard_ping_interval,
43     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
44     Delegate* delegate)
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)),
51       delegate_(delegate) {
52 }
53
54 CloudPrintXmppListener::~CloudPrintXmppListener() {
55   if (push_client_) {
56     push_client_->RemoveObserver(this);
57     push_client_.reset();
58   }
59 }
60
61 void CloudPrintXmppListener::Connect(const std::string& access_token) {
62   access_token_ = access_token;
63   ping_responses_pending_ = 0;
64   ping_scheduled_ = false;
65
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);
71
72   notifier::Subscription subscription;
73   subscription.channel = kCloudPrintPushNotificationsSource;
74   subscription.from = kCloudPrintPushNotificationsSource;
75
76   notifier::SubscriptionList list;
77   list.push_back(subscription);
78
79   push_client_->UpdateSubscriptions(list);
80   push_client_->AddObserver(this);
81   push_client_->UpdateCredentials(robot_email_, access_token_);
82 }
83
84 void CloudPrintXmppListener::set_ping_interval(int interval) {
85   standard_ping_interval_ = interval;
86 }
87
88 void CloudPrintXmppListener::OnNotificationsEnabled() {
89   delegate_->OnXmppConnected();
90   SchedulePing();
91 }
92
93 void CloudPrintXmppListener::OnNotificationsDisabled(
94     notifier::NotificationsDisabledReason reason) {
95   switch (reason) {
96     case notifier::NOTIFICATION_CREDENTIALS_REJECTED:
97       delegate_->OnXmppAuthError();
98       break;
99     case notifier::TRANSIENT_NOTIFICATION_ERROR:
100       Disconnect();
101       break;
102     default:
103       NOTREACHED() << "XMPP failed with unexpected reason code: " << reason;
104   }
105 }
106
107 void CloudPrintXmppListener::OnIncomingNotification(
108     const notifier::Notification& notification) {
109   std::string device_id;
110   std::string path;
111   TokenizeXmppMessage(notification.data, &device_id, &path);
112
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);
119   } else {
120     LOG(ERROR) << "Cannot parse XMPP notification: " << notification.data;
121   }
122 }
123
124 void CloudPrintXmppListener::OnPingResponse() {
125   ping_responses_pending_ = 0;
126   SchedulePing();
127 }
128
129 void CloudPrintXmppListener::Disconnect() {
130   push_client_.reset();
131   delegate_->OnXmppNetworkError();
132 }
133
134 void CloudPrintXmppListener::SchedulePing() {
135   if (ping_scheduled_)
136     return;
137
138   DCHECK_LE(kPingTimeout, kUrgentPingInterval);
139   int delay = (ping_responses_pending_ > 0)
140       ? kUrgentPingInterval - kPingTimeout
141       : standard_ping_interval_;
142
143   delay += base::RandInt(0, delay*kRandomDelayPercentage);
144
145   base::MessageLoop::current()->PostDelayedTask(
146       FROM_HERE,
147       base::Bind(&CloudPrintXmppListener::SendPing, AsWeakPtr()),
148       base::TimeDelta::FromSeconds(delay));
149
150   ping_scheduled_ = true;
151 }
152
153 void CloudPrintXmppListener::SendPing() {
154   ping_scheduled_ = false;
155
156   DCHECK(push_client_);
157   push_client_->SendPing();
158   ++ping_responses_pending_;
159
160   base::MessageLoop::current()->PostDelayedTask(
161         FROM_HERE,
162         base::Bind(&CloudPrintXmppListener::OnPingTimeoutReached, AsWeakPtr()),
163         base::TimeDelta::FromSeconds(kPingTimeout));
164   ++ping_timeouts_posted_;
165 }
166
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.
172
173   switch (ping_responses_pending_) {
174     case 0:
175       break;
176     case 1:
177       SchedulePing();
178       break;
179     case 2:
180       Disconnect();
181       break;
182     default:
183       NOTREACHED();
184   }
185 }
186