This source file includes following definitions.
- frontend_
- InitializeWithToken
- InitializeWithRobotToken
- InitializeWithRobotAuthCode
- Shutdown
- UnregisterPrinters
- pending_xmpp_pings_
- CreateAuthAndConnector
- DestroyAuthAndConnector
- DoInitializeWithToken
- DoInitializeWithRobotToken
- DoInitializeWithRobotAuthCode
- OnAuthenticationComplete
- OnInvalidCredentials
- OnAuthFailed
- OnXmppPingUpdated
- InitNotifications
- DoShutdown
- DoUnregisterPrinters
- HandlePrinterNotification
- PollForJobs
- ScheduleJobPoll
- PingXmppServer
- ScheduleXmppPing
- CheckXmppPingStatus
- GetTokenStore
- NotifyAuthenticated
- NotifyAuthenticationFailed
- NotifyPrintSystemUnavailable
- NotifyUnregisterPrinters
- NotifyXmppPingUpdated
- OnNotificationsEnabled
- OnNotificationsDisabled
- OnIncomingNotification
- OnPingResponse
#include "chrome/service/cloud_print/cloud_print_proxy_backend.h"
#include <map>
#include <vector>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/metrics/histogram.h"
#include "base/rand_util.h"
#include "base/values.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/cloud_print/cloud_print_constants.h"
#include "chrome/service/cloud_print/cloud_print_auth.h"
#include "chrome/service/cloud_print/cloud_print_connector.h"
#include "chrome/service/cloud_print/cloud_print_service_helpers.h"
#include "chrome/service/cloud_print/cloud_print_token_store.h"
#include "chrome/service/cloud_print/connector_settings.h"
#include "chrome/service/net/service_url_request_context.h"
#include "chrome/service/service_process.h"
#include "google_apis/gaia/gaia_oauth_client.h"
#include "google_apis/gaia/gaia_urls.h"
#include "grit/generated_resources.h"
#include "jingle/notifier/base/notifier_options.h"
#include "jingle/notifier/listener/push_client.h"
#include "jingle/notifier/listener/push_client_observer.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
namespace cloud_print {
class CloudPrintProxyBackend::Core
: public base::RefCountedThreadSafe<CloudPrintProxyBackend::Core>,
public CloudPrintAuth::Client,
public CloudPrintConnector::Client,
public notifier::PushClientObserver {
public:
Core(CloudPrintProxyBackend* backend,
const ConnectorSettings& settings,
const gaia::OAuthClientInfo& oauth_client_info,
bool enable_job_poll);
void DoInitializeWithToken(const std::string& cloud_print_token);
void DoInitializeWithRobotToken(const std::string& robot_oauth_refresh_token,
const std::string& robot_email);
void DoInitializeWithRobotAuthCode(const std::string& robot_oauth_auth_code,
const std::string& robot_email);
void DoShutdown();
void DoRegisterSelectedPrinters(
const printing::PrinterList& printer_list);
void DoUnregisterPrinters();
virtual void OnAuthenticationComplete(
const std::string& access_token,
const std::string& robot_oauth_refresh_token,
const std::string& robot_email,
const std::string& user_email) OVERRIDE;
virtual void OnInvalidCredentials() OVERRIDE;
virtual void OnAuthFailed() OVERRIDE;
virtual void OnXmppPingUpdated(int ping_timeout) OVERRIDE;
virtual void OnNotificationsEnabled() OVERRIDE;
virtual void OnNotificationsDisabled(
notifier::NotificationsDisabledReason reason) OVERRIDE;
virtual void OnIncomingNotification(
const notifier::Notification& notification) OVERRIDE;
virtual void OnPingResponse() OVERRIDE;
private:
friend class base::RefCountedThreadSafe<Core>;
virtual ~Core() {}
void CreateAuthAndConnector();
void DestroyAuthAndConnector();
void NotifyPrinterListAvailable(
const printing::PrinterList& printer_list);
void NotifyAuthenticated(
const std::string& robot_oauth_refresh_token,
const std::string& robot_email,
const std::string& user_email);
void NotifyAuthenticationFailed();
void NotifyPrintSystemUnavailable();
void NotifyUnregisterPrinters(const std::string& auth_token,
const std::list<std::string>& printer_ids);
void NotifyXmppPingUpdated(int ping_timeout);
void InitNotifications(const std::string& robot_email,
const std::string& access_token);
void HandlePrinterNotification(const std::string& notification);
void PollForJobs();
void ScheduleJobPoll();
void PingXmppServer();
void ScheduleXmppPing();
void CheckXmppPingStatus();
CloudPrintTokenStore* GetTokenStore();
CloudPrintProxyBackend* backend_;
scoped_refptr<CloudPrintAuth> auth_;
scoped_refptr<CloudPrintConnector> connector_;
gaia::OAuthClientInfo oauth_client_info_;
scoped_ptr<notifier::PushClient> push_client_;
bool notifications_enabled_;
base::TimeTicks notifications_enabled_since_;
bool job_poll_scheduled_;
bool enable_job_poll_;
bool xmpp_ping_scheduled_;
int pending_xmpp_pings_;
ConnectorSettings settings_;
std::string robot_email_;
scoped_ptr<CloudPrintTokenStore> token_store_;
DISALLOW_COPY_AND_ASSIGN(Core);
};
CloudPrintProxyBackend::CloudPrintProxyBackend(
CloudPrintProxyFrontend* frontend,
const ConnectorSettings& settings,
const gaia::OAuthClientInfo& oauth_client_info,
bool enable_job_poll)
: core_thread_("Chrome_CloudPrintProxyCoreThread"),
frontend_loop_(base::MessageLoop::current()),
frontend_(frontend) {
DCHECK(frontend_);
core_ = new Core(this, settings, oauth_client_info, enable_job_poll);
}
CloudPrintProxyBackend::~CloudPrintProxyBackend() { DCHECK(!core_.get()); }
bool CloudPrintProxyBackend::InitializeWithToken(
const std::string& cloud_print_token) {
if (!core_thread_.Start())
return false;
core_thread_.message_loop()->PostTask(
FROM_HERE,
base::Bind(&CloudPrintProxyBackend::Core::DoInitializeWithToken,
core_.get(), cloud_print_token));
return true;
}
bool CloudPrintProxyBackend::InitializeWithRobotToken(
const std::string& robot_oauth_refresh_token,
const std::string& robot_email) {
if (!core_thread_.Start())
return false;
core_thread_.message_loop()->PostTask(
FROM_HERE,
base::Bind(&CloudPrintProxyBackend::Core::DoInitializeWithRobotToken,
core_.get(), robot_oauth_refresh_token, robot_email));
return true;
}
bool CloudPrintProxyBackend::InitializeWithRobotAuthCode(
const std::string& robot_oauth_auth_code,
const std::string& robot_email) {
if (!core_thread_.Start())
return false;
core_thread_.message_loop()->PostTask(
FROM_HERE,
base::Bind(&CloudPrintProxyBackend::Core::DoInitializeWithRobotAuthCode,
core_.get(), robot_oauth_auth_code, robot_email));
return true;
}
void CloudPrintProxyBackend::Shutdown() {
core_thread_.message_loop()->PostTask(
FROM_HERE,
base::Bind(&CloudPrintProxyBackend::Core::DoShutdown, core_.get()));
core_thread_.Stop();
core_ = NULL;
}
void CloudPrintProxyBackend::UnregisterPrinters() {
core_thread_.message_loop()->PostTask(
FROM_HERE,
base::Bind(&CloudPrintProxyBackend::Core::DoUnregisterPrinters,
core_.get()));
}
CloudPrintProxyBackend::Core::Core(
CloudPrintProxyBackend* backend,
const ConnectorSettings& settings,
const gaia::OAuthClientInfo& oauth_client_info,
bool enable_job_poll)
: backend_(backend),
oauth_client_info_(oauth_client_info),
notifications_enabled_(false),
job_poll_scheduled_(false),
enable_job_poll_(enable_job_poll),
xmpp_ping_scheduled_(false),
pending_xmpp_pings_(0) {
settings_.CopyFrom(settings);
}
void CloudPrintProxyBackend::Core::CreateAuthAndConnector() {
if (!auth_.get()) {
auth_ = new CloudPrintAuth(this, settings_.server_url(), oauth_client_info_,
settings_.proxy_id());
}
if (!connector_.get()) {
connector_ = new CloudPrintConnector(this, settings_);
}
}
void CloudPrintProxyBackend::Core::DestroyAuthAndConnector() {
auth_ = NULL;
connector_ = NULL;
}
void CloudPrintProxyBackend::Core::DoInitializeWithToken(
const std::string& cloud_print_token) {
DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
CreateAuthAndConnector();
auth_->AuthenticateWithToken(cloud_print_token);
}
void CloudPrintProxyBackend::Core::DoInitializeWithRobotToken(
const std::string& robot_oauth_refresh_token,
const std::string& robot_email) {
DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
CreateAuthAndConnector();
auth_->AuthenticateWithRobotToken(robot_oauth_refresh_token, robot_email);
}
void CloudPrintProxyBackend::Core::DoInitializeWithRobotAuthCode(
const std::string& robot_oauth_auth_code,
const std::string& robot_email) {
DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
CreateAuthAndConnector();
auth_->AuthenticateWithRobotAuthCode(robot_oauth_auth_code, robot_email);
}
void CloudPrintProxyBackend::Core::OnAuthenticationComplete(
const std::string& access_token,
const std::string& robot_oauth_refresh_token,
const std::string& robot_email,
const std::string& user_email) {
CloudPrintTokenStore* token_store = GetTokenStore();
bool first_time = token_store->token().empty();
token_store->SetToken(access_token);
robot_email_ = robot_email;
backend_->frontend_loop_->PostTask(
FROM_HERE,
base::Bind(&Core::NotifyAuthenticated, this, robot_oauth_refresh_token,
robot_email, user_email));
if (first_time) {
InitNotifications(robot_email, access_token);
} else {
DCHECK(push_client_.get());
push_client_->UpdateCredentials(robot_email, access_token);
}
if (!connector_->IsRunning()) {
if (!connector_->Start()) {
backend_->frontend_loop_->PostTask(
FROM_HERE, base::Bind(&Core::NotifyPrintSystemUnavailable, this));
}
}
}
void CloudPrintProxyBackend::Core::OnInvalidCredentials() {
DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
VLOG(1) << "CP_CONNECTOR: Auth Error";
backend_->frontend_loop_->PostTask(
FROM_HERE, base::Bind(&Core::NotifyAuthenticationFailed, this));
}
void CloudPrintProxyBackend::Core::OnAuthFailed() {
VLOG(1) << "CP_CONNECTOR: Authentication failed in connector.";
if (connector_->IsRunning())
connector_->Stop();
auth_->RefreshAccessToken();
}
void CloudPrintProxyBackend::Core::OnXmppPingUpdated(int ping_timeout) {
settings_.SetXmppPingTimeoutSec(ping_timeout);
backend_->frontend_loop_->PostTask(
FROM_HERE,
base::Bind(&Core::NotifyXmppPingUpdated, this, ping_timeout));
}
void CloudPrintProxyBackend::Core::InitNotifications(
const std::string& robot_email,
const std::string& access_token) {
DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
pending_xmpp_pings_ = 0;
notifier::NotifierOptions notifier_options;
notifier_options.request_context_getter =
g_service_process->GetServiceURLRequestContextGetter();
notifier_options.auth_mechanism = "X-OAUTH2";
notifier_options.try_ssltcp_first = true;
push_client_ = notifier::PushClient::CreateDefault(notifier_options);
push_client_->AddObserver(this);
notifier::Subscription subscription;
subscription.channel = kCloudPrintPushNotificationsSource;
subscription.from = kCloudPrintPushNotificationsSource;
push_client_->UpdateSubscriptions(
notifier::SubscriptionList(1, subscription));
push_client_->UpdateCredentials(robot_email, access_token);
}
void CloudPrintProxyBackend::Core::DoShutdown() {
DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
VLOG(1) << "CP_CONNECTOR: Shutdown connector, id: " << settings_.proxy_id();
if (connector_->IsRunning())
connector_->Stop();
if (push_client_.get()) {
push_client_->RemoveObserver(this);
}
push_client_.reset();
notifications_enabled_ = false;
notifications_enabled_since_ = base::TimeTicks();
token_store_.reset();
DestroyAuthAndConnector();
}
void CloudPrintProxyBackend::Core::DoUnregisterPrinters() {
DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
std::string access_token = GetTokenStore()->token();
std::list<std::string> printer_ids;
connector_->GetPrinterIds(&printer_ids);
backend_->frontend_loop_->PostTask(
FROM_HERE,
base::Bind(&Core::NotifyUnregisterPrinters,
this, access_token, printer_ids));
}
void CloudPrintProxyBackend::Core::HandlePrinterNotification(
const std::string& notification) {
DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
size_t pos = notification.rfind(kNotificationUpdateSettings);
if (pos == std::string::npos) {
VLOG(1) << "CP_CONNECTOR: Handle printer notification, id: "
<< notification;
connector_->CheckForJobs(kJobFetchReasonNotified, notification);
} else {
DCHECK(pos == notification.length() - strlen(kNotificationUpdateSettings));
std::string printer_id = notification.substr(0, pos);
VLOG(1) << "CP_CONNECTOR: Update printer settings, id: " << printer_id;
connector_->UpdatePrinterSettings(printer_id);
}
}
void CloudPrintProxyBackend::Core::PollForJobs() {
VLOG(1) << "CP_CONNECTOR: Polling for jobs.";
DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
connector_->CheckForJobs(kJobFetchReasonPoll, std::string());
job_poll_scheduled_ = false;
if (!notifications_enabled_ && enable_job_poll_)
ScheduleJobPoll();
}
void CloudPrintProxyBackend::Core::ScheduleJobPoll() {
if (!job_poll_scheduled_) {
base::TimeDelta interval = base::TimeDelta::FromSeconds(
base::RandInt(kMinJobPollIntervalSecs, kMaxJobPollIntervalSecs));
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&CloudPrintProxyBackend::Core::PollForJobs, this),
interval);
job_poll_scheduled_ = true;
}
}
void CloudPrintProxyBackend::Core::PingXmppServer() {
xmpp_ping_scheduled_ = false;
if (!push_client_.get())
return;
push_client_->SendPing();
pending_xmpp_pings_++;
if (pending_xmpp_pings_ >= kMaxFailedXmppPings) {
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&CloudPrintProxyBackend::Core::CheckXmppPingStatus, this),
base::TimeDelta::FromSeconds(kXmppPingCheckIntervalSecs));
}
if (notifications_enabled_)
ScheduleXmppPing();
}
void CloudPrintProxyBackend::Core::ScheduleXmppPing() {
if (!xmpp_ping_scheduled_) {
base::TimeDelta interval = base::TimeDelta::FromSeconds(
base::RandInt(settings_.xmpp_ping_timeout_sec() * 0.9,
settings_.xmpp_ping_timeout_sec() * 1.1));
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&CloudPrintProxyBackend::Core::PingXmppServer, this),
interval);
xmpp_ping_scheduled_ = true;
}
}
void CloudPrintProxyBackend::Core::CheckXmppPingStatus() {
if (pending_xmpp_pings_ >= kMaxFailedXmppPings) {
UMA_HISTOGRAM_COUNTS_100("CloudPrint.XmppPingTry", 99);
pending_xmpp_pings_ = 0;
push_client_.reset();
InitNotifications(robot_email_, GetTokenStore()->token());
}
}
CloudPrintTokenStore* CloudPrintProxyBackend::Core::GetTokenStore() {
DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
if (!token_store_.get())
token_store_.reset(new CloudPrintTokenStore);
return token_store_.get();
}
void CloudPrintProxyBackend::Core::NotifyAuthenticated(
const std::string& robot_oauth_refresh_token,
const std::string& robot_email,
const std::string& user_email) {
DCHECK(base::MessageLoop::current() == backend_->frontend_loop_);
backend_->frontend_
->OnAuthenticated(robot_oauth_refresh_token, robot_email, user_email);
}
void CloudPrintProxyBackend::Core::NotifyAuthenticationFailed() {
DCHECK(base::MessageLoop::current() == backend_->frontend_loop_);
backend_->frontend_->OnAuthenticationFailed();
}
void CloudPrintProxyBackend::Core::NotifyPrintSystemUnavailable() {
DCHECK(base::MessageLoop::current() == backend_->frontend_loop_);
backend_->frontend_->OnPrintSystemUnavailable();
}
void CloudPrintProxyBackend::Core::NotifyUnregisterPrinters(
const std::string& auth_token,
const std::list<std::string>& printer_ids) {
DCHECK(base::MessageLoop::current() == backend_->frontend_loop_);
backend_->frontend_->OnUnregisterPrinters(auth_token, printer_ids);
}
void CloudPrintProxyBackend::Core::NotifyXmppPingUpdated(int ping_timeout) {
DCHECK(base::MessageLoop::current() == backend_->frontend_loop_);
backend_->frontend_->OnXmppPingUpdated(ping_timeout);
}
void CloudPrintProxyBackend::Core::OnNotificationsEnabled() {
DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
notifications_enabled_ = true;
notifications_enabled_since_ = base::TimeTicks::Now();
VLOG(1) << "Notifications for connector " << settings_.proxy_id()
<< " were enabled at "
<< notifications_enabled_since_.ToInternalValue();
ScheduleJobPoll();
ScheduleXmppPing();
}
void CloudPrintProxyBackend::Core::OnNotificationsDisabled(
notifier::NotificationsDisabledReason reason) {
DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
notifications_enabled_ = false;
LOG(ERROR) << "Notifications for connector " << settings_.proxy_id()
<< " disabled.";
notifications_enabled_since_ = base::TimeTicks();
if (enable_job_poll_)
ScheduleJobPoll();
}
void CloudPrintProxyBackend::Core::OnIncomingNotification(
const notifier::Notification& notification) {
pending_xmpp_pings_ = 0;
DCHECK(base::MessageLoop::current() == backend_->core_thread_.message_loop());
VLOG(1) << "CP_CONNECTOR: Incoming notification.";
if (0 == base::strcasecmp(kCloudPrintPushNotificationsSource,
notification.channel.c_str()))
HandlePrinterNotification(notification.data);
}
void CloudPrintProxyBackend::Core::OnPingResponse() {
UMA_HISTOGRAM_COUNTS_100("CloudPrint.XmppPingTry", pending_xmpp_pings_);
pending_xmpp_pings_ = 0;
VLOG(1) << "CP_CONNECTOR: Ping response received.";
}
}