Home | History | Annotate | Download | only in cloud_print
      1 // Copyright (c) 2012 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 "chrome/service/cloud_print/cloud_print_proxy_backend.h"
      6 
      7 #include <map>
      8 #include <vector>
      9 
     10 #include "base/bind.h"
     11 #include "base/command_line.h"
     12 #include "base/compiler_specific.h"
     13 #include "base/metrics/histogram.h"
     14 #include "base/rand_util.h"
     15 #include "base/values.h"
     16 #include "chrome/common/cloud_print/cloud_print_constants.h"
     17 #include "chrome/service/cloud_print/cloud_print_auth.h"
     18 #include "chrome/service/cloud_print/cloud_print_connector.h"
     19 #include "chrome/service/cloud_print/cloud_print_service_helpers.h"
     20 #include "chrome/service/cloud_print/cloud_print_token_store.h"
     21 #include "chrome/service/cloud_print/connector_settings.h"
     22 #include "chrome/service/net/service_url_request_context.h"
     23 #include "chrome/service/service_process.h"
     24 #include "components/cloud_devices/common/cloud_devices_switches.h"
     25 #include "google_apis/gaia/gaia_oauth_client.h"
     26 #include "google_apis/gaia/gaia_urls.h"
     27 #include "grit/generated_resources.h"
     28 #include "jingle/notifier/base/notifier_options.h"
     29 #include "jingle/notifier/listener/push_client.h"
     30 #include "jingle/notifier/listener/push_client_observer.h"
     31 #include "ui/base/l10n/l10n_util.h"
     32 #include "url/gurl.h"
     33 
     34 namespace cloud_print {
     35 
     36 // The real guts of CloudPrintProxyBackend, to keep the public client API clean.
     37 class CloudPrintProxyBackend::Core
     38     : public base::RefCountedThreadSafe<CloudPrintProxyBackend::Core>,
     39       public CloudPrintAuth::Client,
     40       public CloudPrintConnector::Client,
     41       public notifier::PushClientObserver {
     42  public:
     43   // It is OK for print_server_url to be empty. In this case system should
     44   // use system default (local) print server.
     45   Core(CloudPrintProxyBackend* backend,
     46        const ConnectorSettings& settings,
     47        const gaia::OAuthClientInfo& oauth_client_info,
     48        bool enable_job_poll);
     49 
     50   // Note:
     51   //
     52   // The Do* methods are the various entry points from CloudPrintProxyBackend
     53   // It calls us on a dedicated thread to actually perform synchronous
     54   // (and potentially blocking) operations.
     55   void DoInitializeWithToken(const std::string& cloud_print_token);
     56   void DoInitializeWithRobotToken(const std::string& robot_oauth_refresh_token,
     57                                   const std::string& robot_email);
     58   void DoInitializeWithRobotAuthCode(const std::string& robot_oauth_auth_code,
     59                                      const std::string& robot_email);
     60 
     61   // Called on the CloudPrintProxyBackend core_thread_ to perform
     62   // shutdown.
     63   void DoShutdown();
     64   void DoRegisterSelectedPrinters(
     65       const printing::PrinterList& printer_list);
     66   void DoUnregisterPrinters();
     67 
     68   // CloudPrintAuth::Client implementation.
     69   virtual void OnAuthenticationComplete(
     70       const std::string& access_token,
     71       const std::string& robot_oauth_refresh_token,
     72       const std::string& robot_email,
     73       const std::string& user_email) OVERRIDE;
     74   virtual void OnInvalidCredentials() OVERRIDE;
     75 
     76   // CloudPrintConnector::Client implementation.
     77   virtual void OnAuthFailed() OVERRIDE;
     78   virtual void OnXmppPingUpdated(int ping_timeout) OVERRIDE;
     79 
     80   // notifier::PushClientObserver implementation.
     81   virtual void OnNotificationsEnabled() OVERRIDE;
     82   virtual void OnNotificationsDisabled(
     83       notifier::NotificationsDisabledReason reason) OVERRIDE;
     84   virtual void OnIncomingNotification(
     85       const notifier::Notification& notification) OVERRIDE;
     86   virtual void OnPingResponse() OVERRIDE;
     87 
     88  private:
     89   friend class base::RefCountedThreadSafe<Core>;
     90 
     91   virtual ~Core() {}
     92 
     93   void CreateAuthAndConnector();
     94   void DestroyAuthAndConnector();
     95 
     96   // NotifyXXX is how the Core communicates with the frontend across
     97   // threads.
     98   void NotifyPrinterListAvailable(
     99       const printing::PrinterList& printer_list);
    100   void NotifyAuthenticated(
    101     const std::string& robot_oauth_refresh_token,
    102     const std::string& robot_email,
    103     const std::string& user_email);
    104   void NotifyAuthenticationFailed();
    105   void NotifyPrintSystemUnavailable();
    106   void NotifyUnregisterPrinters(const std::string& auth_token,
    107                                 const std::list<std::string>& printer_ids);
    108   void NotifyXmppPingUpdated(int ping_timeout);
    109 
    110   // Init XMPP channel
    111   void InitNotifications(const std::string& robot_email,
    112                          const std::string& access_token);
    113 
    114   void HandlePrinterNotification(const std::string& notification);
    115   void PollForJobs();
    116   // Schedules a task to poll for jobs. Does nothing if a task is already
    117   // scheduled.
    118   void ScheduleJobPoll();
    119   void PingXmppServer();
    120   void ScheduleXmppPing();
    121   void CheckXmppPingStatus();
    122 
    123   CloudPrintTokenStore* GetTokenStore();
    124 
    125   // Our parent CloudPrintProxyBackend
    126   CloudPrintProxyBackend* backend_;
    127 
    128   // Cloud Print authenticator.
    129   scoped_refptr<CloudPrintAuth> auth_;
    130 
    131   // Cloud Print connector.
    132   scoped_refptr<CloudPrintConnector> connector_;
    133 
    134   // OAuth client info.
    135   gaia::OAuthClientInfo oauth_client_info_;
    136   // Notification (xmpp) handler.
    137   scoped_ptr<notifier::PushClient> push_client_;
    138   // Indicates whether XMPP notifications are currently enabled.
    139   bool notifications_enabled_;
    140   // The time when notifications were enabled. Valid only when
    141   // notifications_enabled_ is true.
    142   base::TimeTicks notifications_enabled_since_;
    143   // Indicates whether a task to poll for jobs has been scheduled.
    144   bool job_poll_scheduled_;
    145   // Indicates whether we should poll for jobs when we lose XMPP connection.
    146   bool enable_job_poll_;
    147   // Indicates whether a task to ping xmpp server has been scheduled.
    148   bool xmpp_ping_scheduled_;
    149   // Number of XMPP pings pending reply from the server.
    150   int pending_xmpp_pings_;
    151   // Connector settings.
    152   ConnectorSettings settings_;
    153   std::string robot_email_;
    154   scoped_ptr<CloudPrintTokenStore> token_store_;
    155 
    156   DISALLOW_COPY_AND_ASSIGN(Core);
    157 };
    158 
    159 CloudPrintProxyBackend::CloudPrintProxyBackend(
    160     CloudPrintProxyFrontend* frontend,
    161     const ConnectorSettings& settings,
    162     const gaia::OAuthClientInfo& oauth_client_info,
    163     bool enable_job_poll)
    164     : core_thread_("Chrome_CloudPrintProxyCoreThread"),
    165       frontend_loop_(base::MessageLoop::current()),
    166       frontend_(frontend) {
    167   DCHECK(frontend_);
    168   core_ = new Core(this, settings, oauth_client_info, enable_job_poll);
    169 }
    170 
    171 CloudPrintProxyBackend::~CloudPrintProxyBackend() { DCHECK(!core_.get()); }
    172 
    173 bool CloudPrintProxyBackend::InitializeWithToken(
    174     const std::string& cloud_print_token) {
    175   if (!core_thread_.Start())
    176     return false;
    177   core_thread_.message_loop()->PostTask(
    178       FROM_HERE,
    179       base::Bind(&CloudPrintProxyBackend::Core::DoInitializeWithToken,
    180                  core_.get(), cloud_print_token));
    181   return true;
    182 }
    183 
    184 bool CloudPrintProxyBackend::InitializeWithRobotToken(
    185     const std::string& robot_oauth_refresh_token,
    186     const std::string& robot_email) {
    187   if (!core_thread_.Start())
    188     return false;
    189   core_thread_.message_loop()->PostTask(
    190       FROM_HERE,
    191       base::Bind(&CloudPrintProxyBackend::Core::DoInitializeWithRobotToken,
    192                  core_.get(), robot_oauth_refresh_token, robot_email));
    193   return true;
    194 }
    195 
    196 bool CloudPrintProxyBackend::InitializeWithRobotAuthCode(
    197     const std::string& robot_oauth_auth_code,
    198     const std::string& robot_email) {
    199   if (!core_thread_.Start())
    200     return false;
    201   core_thread_.message_loop()->PostTask(
    202       FROM_HERE,
    203       base::Bind(&CloudPrintProxyBackend::Core::DoInitializeWithRobotAuthCode,
    204                  core_.get(), robot_oauth_auth_code, robot_email));
    205   return true;
    206 }
    207 
    208 void CloudPrintProxyBackend::Shutdown() {
    209   core_thread_.message_loop()->PostTask(
    210       FROM_HERE,
    211       base::Bind(&CloudPrintProxyBackend::Core::DoShutdown, core_.get()));
    212   core_thread_.Stop();
    213   core_ = NULL;  // Releases reference to core_.
    214 }
    215 
    216 void CloudPrintProxyBackend::UnregisterPrinters() {
    217   core_thread_.message_loop()->PostTask(
    218       FROM_HERE,
    219       base::Bind(&CloudPrintProxyBackend::Core::DoUnregisterPrinters,
    220                  core_.get()));
    221 }
    222 
    223 CloudPrintProxyBackend::Core::Core(
    224     CloudPrintProxyBackend* backend,
    225     const ConnectorSettings& settings,
    226     const gaia::OAuthClientInfo& oauth_client_info,
    227     bool enable_job_poll)
    228       : backend_(backend),
    229         oauth_client_info_(oauth_client_info),
    230         notifications_enabled_(false),
    231         job_poll_scheduled_(false),
    232         enable_job_poll_(enable_job_poll),
    233         xmpp_ping_scheduled_(false),
    234         pending_xmpp_pings_(0) {
    235   settings_.CopyFrom(settings);
    236 }
    237 
    238 void CloudPrintProxyBackend::Core::CreateAuthAndConnector() {
    239   if (!auth_.get()) {
    240     auth_ = new CloudPrintAuth(this, settings_.server_url(), oauth_client_info_,
    241                                settings_.proxy_id());
    242   }
    243 
    244   if (!connector_.get()) {
    245     connector_ = new CloudPrintConnector(this, settings_);
    246   }
    247 }
    248 
    249 void CloudPrintProxyBackend::Core::DestroyAuthAndConnector() {
    250   auth_ = NULL;
    251   connector_ = NULL;
    252 }
    253 
    254 void CloudPrintProxyBackend::Core::DoInitializeWithToken(
    255     const std::string& cloud_print_token) {
    256   DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
    257   CreateAuthAndConnector();
    258   auth_->AuthenticateWithToken(cloud_print_token);
    259 }
    260 
    261 void CloudPrintProxyBackend::Core::DoInitializeWithRobotToken(
    262     const std::string& robot_oauth_refresh_token,
    263     const std::string& robot_email) {
    264   DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
    265   CreateAuthAndConnector();
    266   auth_->AuthenticateWithRobotToken(robot_oauth_refresh_token, robot_email);
    267 }
    268 
    269 void CloudPrintProxyBackend::Core::DoInitializeWithRobotAuthCode(
    270     const std::string& robot_oauth_auth_code,
    271     const std::string& robot_email) {
    272   DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
    273   CreateAuthAndConnector();
    274   auth_->AuthenticateWithRobotAuthCode(robot_oauth_auth_code, robot_email);
    275 }
    276 
    277 void CloudPrintProxyBackend::Core::OnAuthenticationComplete(
    278     const std::string& access_token,
    279     const std::string& robot_oauth_refresh_token,
    280     const std::string& robot_email,
    281     const std::string& user_email) {
    282   CloudPrintTokenStore* token_store  = GetTokenStore();
    283   bool first_time = token_store->token().empty();
    284   token_store->SetToken(access_token);
    285   robot_email_ = robot_email;
    286   // Let the frontend know that we have authenticated.
    287   backend_->frontend_loop_->PostTask(
    288       FROM_HERE,
    289       base::Bind(&Core::NotifyAuthenticated, this, robot_oauth_refresh_token,
    290                  robot_email, user_email));
    291   if (first_time) {
    292     InitNotifications(robot_email, access_token);
    293   } else {
    294     // If we are refreshing a token, update the XMPP token too.
    295     DCHECK(push_client_.get());
    296     push_client_->UpdateCredentials(robot_email, access_token);
    297   }
    298   // Start cloud print connector if needed.
    299   if (!connector_->IsRunning()) {
    300     if (!connector_->Start()) {
    301       // Let the frontend know that we do not have a print system.
    302       backend_->frontend_loop_->PostTask(
    303           FROM_HERE, base::Bind(&Core::NotifyPrintSystemUnavailable, this));
    304     }
    305   }
    306 }
    307 
    308 void CloudPrintProxyBackend::Core::OnInvalidCredentials() {
    309   DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
    310   VLOG(1) << "CP_CONNECTOR: Auth Error";
    311   backend_->frontend_loop_->PostTask(
    312       FROM_HERE, base::Bind(&Core::NotifyAuthenticationFailed, this));
    313 }
    314 
    315 void CloudPrintProxyBackend::Core::OnAuthFailed() {
    316   VLOG(1) << "CP_CONNECTOR: Authentication failed in connector.";
    317   // Let's stop connecter and refresh token. We'll restart connecter once
    318   // new token available.
    319   if (connector_->IsRunning())
    320     connector_->Stop();
    321 
    322   // Refresh Auth token.
    323   auth_->RefreshAccessToken();
    324 }
    325 
    326 void CloudPrintProxyBackend::Core::OnXmppPingUpdated(int ping_timeout) {
    327   settings_.SetXmppPingTimeoutSec(ping_timeout);
    328   backend_->frontend_loop_->PostTask(
    329       FROM_HERE,
    330       base::Bind(&Core::NotifyXmppPingUpdated, this, ping_timeout));
    331 }
    332 
    333 void CloudPrintProxyBackend::Core::InitNotifications(
    334     const std::string& robot_email,
    335     const std::string& access_token) {
    336   DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
    337 
    338   pending_xmpp_pings_ = 0;
    339   notifier::NotifierOptions notifier_options;
    340   notifier_options.request_context_getter =
    341       g_service_process->GetServiceURLRequestContextGetter();
    342   notifier_options.auth_mechanism = "X-OAUTH2";
    343   notifier_options.try_ssltcp_first = true;
    344   notifier_options.xmpp_host_port = net::HostPortPair::FromString(
    345       CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
    346           switches::kCloudPrintXmppEndpoint));
    347   push_client_ = notifier::PushClient::CreateDefault(notifier_options);
    348   push_client_->AddObserver(this);
    349   notifier::Subscription subscription;
    350   subscription.channel = kCloudPrintPushNotificationsSource;
    351   subscription.from = kCloudPrintPushNotificationsSource;
    352   push_client_->UpdateSubscriptions(
    353       notifier::SubscriptionList(1, subscription));
    354   push_client_->UpdateCredentials(robot_email, access_token);
    355 }
    356 
    357 void CloudPrintProxyBackend::Core::DoShutdown() {
    358   DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
    359   VLOG(1) << "CP_CONNECTOR: Shutdown connector, id: " << settings_.proxy_id();
    360 
    361   if (connector_->IsRunning())
    362     connector_->Stop();
    363 
    364   // Important to delete the PushClient on this thread.
    365   if (push_client_.get()) {
    366     push_client_->RemoveObserver(this);
    367   }
    368   push_client_.reset();
    369   notifications_enabled_ = false;
    370   notifications_enabled_since_ = base::TimeTicks();
    371   token_store_.reset();
    372 
    373   DestroyAuthAndConnector();
    374 }
    375 
    376 void CloudPrintProxyBackend::Core::DoUnregisterPrinters() {
    377   DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
    378 
    379   std::string access_token = GetTokenStore()->token();
    380 
    381   std::list<std::string> printer_ids;
    382   connector_->GetPrinterIds(&printer_ids);
    383 
    384   backend_->frontend_loop_->PostTask(
    385       FROM_HERE,
    386       base::Bind(&Core::NotifyUnregisterPrinters,
    387                  this, access_token, printer_ids));
    388 }
    389 
    390 void CloudPrintProxyBackend::Core::HandlePrinterNotification(
    391     const std::string& notification) {
    392   DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
    393 
    394   size_t pos = notification.rfind(kNotificationUpdateSettings);
    395   if (pos == std::string::npos) {
    396     VLOG(1) << "CP_CONNECTOR: Handle printer notification, id: "
    397             << notification;
    398     connector_->CheckForJobs(kJobFetchReasonNotified, notification);
    399   } else {
    400     DCHECK(pos == notification.length() - strlen(kNotificationUpdateSettings));
    401     std::string printer_id = notification.substr(0, pos);
    402     VLOG(1) << "CP_CONNECTOR: Update printer settings, id: " << printer_id;
    403     connector_->UpdatePrinterSettings(printer_id);
    404   }
    405 }
    406 
    407 void CloudPrintProxyBackend::Core::PollForJobs() {
    408   VLOG(1) << "CP_CONNECTOR: Polling for jobs.";
    409   DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
    410   // Check all printers for jobs.
    411   connector_->CheckForJobs(kJobFetchReasonPoll, std::string());
    412 
    413   job_poll_scheduled_ = false;
    414   // If we don't have notifications and job polling is enabled, poll again
    415   // after a while.
    416   if (!notifications_enabled_ && enable_job_poll_)
    417     ScheduleJobPoll();
    418 }
    419 
    420 void CloudPrintProxyBackend::Core::ScheduleJobPoll() {
    421   if (!job_poll_scheduled_) {
    422     base::TimeDelta interval = base::TimeDelta::FromSeconds(
    423         base::RandInt(kMinJobPollIntervalSecs, kMaxJobPollIntervalSecs));
    424     base::MessageLoop::current()->PostDelayedTask(
    425         FROM_HERE,
    426         base::Bind(&CloudPrintProxyBackend::Core::PollForJobs, this),
    427         interval);
    428     job_poll_scheduled_ = true;
    429   }
    430 }
    431 
    432 void CloudPrintProxyBackend::Core::PingXmppServer() {
    433   xmpp_ping_scheduled_ = false;
    434 
    435   if (!push_client_.get())
    436     return;
    437 
    438   push_client_->SendPing();
    439 
    440   pending_xmpp_pings_++;
    441   if (pending_xmpp_pings_ >= kMaxFailedXmppPings) {
    442     // Check ping status when we close to the limit.
    443     base::MessageLoop::current()->PostDelayedTask(
    444         FROM_HERE,
    445         base::Bind(&CloudPrintProxyBackend::Core::CheckXmppPingStatus, this),
    446         base::TimeDelta::FromSeconds(kXmppPingCheckIntervalSecs));
    447   }
    448 
    449   // Schedule next ping if needed.
    450   if (notifications_enabled_)
    451     ScheduleXmppPing();
    452 }
    453 
    454 void CloudPrintProxyBackend::Core::ScheduleXmppPing() {
    455   // settings_.xmpp_ping_enabled() is obsolete, we are now control
    456   // XMPP pings from Cloud Print server.
    457   if (!xmpp_ping_scheduled_) {
    458     base::TimeDelta interval = base::TimeDelta::FromSeconds(
    459       base::RandInt(settings_.xmpp_ping_timeout_sec() * 0.9,
    460                     settings_.xmpp_ping_timeout_sec() * 1.1));
    461     base::MessageLoop::current()->PostDelayedTask(
    462         FROM_HERE,
    463         base::Bind(&CloudPrintProxyBackend::Core::PingXmppServer, this),
    464         interval);
    465     xmpp_ping_scheduled_ = true;
    466   }
    467 }
    468 
    469 void CloudPrintProxyBackend::Core::CheckXmppPingStatus() {
    470   if (pending_xmpp_pings_ >= kMaxFailedXmppPings) {
    471     UMA_HISTOGRAM_COUNTS_100("CloudPrint.XmppPingTry", 99);  // Max on fail.
    472     // Reconnect to XMPP.
    473     pending_xmpp_pings_ = 0;
    474     push_client_.reset();
    475     InitNotifications(robot_email_, GetTokenStore()->token());
    476   }
    477 }
    478 
    479 CloudPrintTokenStore* CloudPrintProxyBackend::Core::GetTokenStore() {
    480   DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
    481   if (!token_store_.get())
    482     token_store_.reset(new CloudPrintTokenStore);
    483   return token_store_.get();
    484 }
    485 
    486 void CloudPrintProxyBackend::Core::NotifyAuthenticated(
    487     const std::string& robot_oauth_refresh_token,
    488     const std::string& robot_email,
    489     const std::string& user_email) {
    490   DCHECK(base::MessageLoop::current() == backend_->frontend_loop_);
    491   backend_->frontend_
    492       ->OnAuthenticated(robot_oauth_refresh_token, robot_email, user_email);
    493 }
    494 
    495 void CloudPrintProxyBackend::Core::NotifyAuthenticationFailed() {
    496   DCHECK(base::MessageLoop::current() == backend_->frontend_loop_);
    497   backend_->frontend_->OnAuthenticationFailed();
    498 }
    499 
    500 void CloudPrintProxyBackend::Core::NotifyPrintSystemUnavailable() {
    501   DCHECK(base::MessageLoop::current() == backend_->frontend_loop_);
    502   backend_->frontend_->OnPrintSystemUnavailable();
    503 }
    504 
    505 void CloudPrintProxyBackend::Core::NotifyUnregisterPrinters(
    506     const std::string& auth_token,
    507     const std::list<std::string>& printer_ids) {
    508   DCHECK(base::MessageLoop::current() == backend_->frontend_loop_);
    509   backend_->frontend_->OnUnregisterPrinters(auth_token, printer_ids);
    510 }
    511 
    512 void CloudPrintProxyBackend::Core::NotifyXmppPingUpdated(int ping_timeout) {
    513   DCHECK(base::MessageLoop::current() == backend_->frontend_loop_);
    514   backend_->frontend_->OnXmppPingUpdated(ping_timeout);
    515 }
    516 
    517 void CloudPrintProxyBackend::Core::OnNotificationsEnabled() {
    518   DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
    519   notifications_enabled_ = true;
    520   notifications_enabled_since_ = base::TimeTicks::Now();
    521   VLOG(1) << "Notifications for connector " << settings_.proxy_id()
    522           << " were enabled at "
    523           << notifications_enabled_since_.ToInternalValue();
    524   // Notifications just got re-enabled. In this case we want to schedule
    525   // a poll once for jobs we might have missed when we were dark.
    526   // Note that ScheduleJobPoll will not schedule again if a job poll task is
    527   // already scheduled.
    528   ScheduleJobPoll();
    529 
    530   // Schedule periodic ping for XMPP notification channel.
    531   ScheduleXmppPing();
    532 }
    533 
    534 void CloudPrintProxyBackend::Core::OnNotificationsDisabled(
    535     notifier::NotificationsDisabledReason reason) {
    536   DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
    537   notifications_enabled_ = false;
    538   LOG(ERROR) << "Notifications for connector " << settings_.proxy_id()
    539              << " disabled.";
    540   notifications_enabled_since_ = base::TimeTicks();
    541   // We just lost notifications. This this case we want to schedule a
    542   // job poll if enable_job_poll_ is true.
    543   if (enable_job_poll_)
    544     ScheduleJobPoll();
    545 }
    546 
    547 
    548 void CloudPrintProxyBackend::Core::OnIncomingNotification(
    549     const notifier::Notification& notification) {
    550   // Since we got some notification from the server,
    551   // reset pending ping counter to 0.
    552   pending_xmpp_pings_ = 0;
    553 
    554   DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
    555   VLOG(1) << "CP_CONNECTOR: Incoming notification.";
    556   if (0 == base::strcasecmp(kCloudPrintPushNotificationsSource,
    557                             notification.channel.c_str()))
    558     HandlePrinterNotification(notification.data);
    559 }
    560 
    561 void CloudPrintProxyBackend::Core::OnPingResponse() {
    562   UMA_HISTOGRAM_COUNTS_100("CloudPrint.XmppPingTry", pending_xmpp_pings_);
    563   pending_xmpp_pings_ = 0;
    564   VLOG(1) << "CP_CONNECTOR: Ping response received.";
    565 }
    566 
    567 }  // namespace cloud_print
    568