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