Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3810)

Unified Diff: chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_receive_service.cc

Issue 11038063: Support chrome_to_mobile job receiving Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix format Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_receive_service.cc
diff --git a/chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_receive_service.cc b/chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_receive_service.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8f211227224dac429f6bed750187fb4d171c60ff
--- /dev/null
+++ b/chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_receive_service.cc
@@ -0,0 +1,544 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_receive_service.h"
+
+#include "chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_receive_util.h"
+#include "chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_receiving_device_info_handler.h"
+#include "chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_receiving_device_info_handler_ios.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/oauth2_token_service.h"
+#include "chrome/browser/signin/oauth2_token_service_factory.h"
+#include "chrome/browser/signin/token_service.h"
+#include "chrome/browser/signin/token_service_factory.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/pref_names.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
+#include "googleurl/src/gurl.h"
+#include "google_apis/gaia/gaia_constants.h"
+
+ChromeToMobileReceiveService::ChromeToMobileReceiveService(Profile* profile)
+ : profile_(profile),
+ backend_(new chrome_to_mobile_receive::ChromeToMobileReceiveBackend(
+ this,
+ chrome_to_mobile_receive::GetCloudPrintServerURL(),
+ profile_->GetRequestContext(),
+ OAuth2TokenServiceFactory::GetForProfile(profile_))),
+#if defined(OS_IOS)
+ device_info_handler_(new chrome_to_mobile_receive::
+ ChromeToMobileReceivingDeviceInfoHandlerIOS()),
+#else
+ device_info_handler_(new chrome_to_mobile_receive::
+ ChromeToMobileReceivingDeviceInfoHandler()),
+#endif // defined(OS_IOS)
+ authenticated_(false),
+ failure_type_(kNoFailure),
+ status_(kNotStarted),
+ should_start_device_when_not_started_(false),
+ should_fetch_jobs_when_started_(false) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
+ registrar_.Add(this,
+ chrome::NOTIFICATION_TOKEN_AVAILABLE,
+ content::Source<TokenService>(token_service));
+ registrar_.Add(this,
+ chrome::NOTIFICATION_GOOGLE_WILL_SIGN_OUT,
+ content::Source<Profile>(profile_));
+
+ PrefService* pref_service = profile_->GetPrefs();
+ if (!pref_service->FindPreference(prefs::kChromeToMobileReceiveEnabled)) {
+ pref_service->RegisterBooleanPref(prefs::kChromeToMobileReceiveEnabled,
+ true,
+ PrefService::UNSYNCABLE_PREF);
+ }
+ if (!pref_service->FindPreference(
+ prefs::kChromeToMobileReceiveStartingSuppressed)) {
+ pref_service->RegisterBooleanPref(
+ prefs::kChromeToMobileReceiveStartingSuppressed,
+ false,
+ PrefService::UNSYNCABLE_PREF);
+ }
+ if (!pref_service->FindPreference(prefs::kChromeToMobileReceivePrinterId)) {
+ pref_service->RegisterStringPref(prefs::kChromeToMobileReceivePrinterId,
+ "",
+ PrefService::UNSYNCABLE_PREF);
+ }
+}
+
+ChromeToMobileReceiveService::~ChromeToMobileReceiveService() {
+ registrar_.RemoveAll();
+}
+
+void ChromeToMobileReceiveService::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ switch (type) {
+ case chrome::NOTIFICATION_GOOGLE_WILL_SIGN_OUT: {
+ authenticated_ = false;
+ failure_type_ = kNoFailure;
+ if (status_ == kNotStarted && backend_->IsRunning()) {
+ // Shuts down |backend_|.
+ backend_->ShutDown();
+ return;
+ }
+ // |backend_| will be shut down when |StopDevice()| completes.
+ StopDevice();
+ return;
+ }
+
+ case chrome::NOTIFICATION_TOKEN_AVAILABLE: {
+ const TokenService::TokenAvailableDetails* token_details =
+ content::Details<const TokenService::TokenAvailableDetails>(
+ details).ptr();
+ if (!token_details || token_details->service() !=
+ GaiaConstants::kGaiaOAuth2LoginRefreshToken) {
+ return;
+ }
+
+ DCHECK(IsAuthenticationReady());
+ // Starts the device and fetches jobs if the service is ready.
+ // The if-condition on |authenticated_| is to avoid doing it too often
+ // in the case where multiple authentication notifications are received
+ // without user signing out.
+ if (authenticated_)
+ return;
+
+ authenticated_ = true;
+ if (IsServiceReadyToBeStarted()) {
+ StartDeviceAndFetchAll();
+ } else {
+ // Turns on this device if it was turned off due to failure.
+ PrefService* pref_service = profile_->GetPrefs();
+ if (!pref_service->GetBoolean(prefs::kChromeToMobileReceiveEnabled) &&
+ pref_service->GetBoolean(
+ prefs::kChromeToMobileReceiveStartingSuppressed)) {
+ TurnOnPreference(true);
+ return;
+ }
+ if (ShouldFetchDeviceInformation())
+ device_info_handler_->Start();
+ }
+
+ // Informs service status observers that credientials are updated.
+ UpdateServiceObservers();
+ return;
+ }
+
+ default: {
+ NOTREACHED();
+ }
+ }
+}
+
+void ChromeToMobileReceiveService::TurnOnPreference(bool on) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ profile_->GetPrefs()->SetBoolean(
+ prefs::kChromeToMobileReceiveStartingSuppressed, false);
+ profile_->GetPrefs()->SetBoolean(prefs::kChromeToMobileReceiveEnabled, on);
+ if (!on) {
+ // Note |failure_type_| is not reset here to surface the error when the
+ // preference is turned off. Note error might be received when stopping
+ // The device.
+ StopDevice();
+ return;
+ }
+
+ failure_type_ = kNoFailure;
+ if (IsServiceReadyToBeStarted()) {
+ StartDeviceAndFetchAll();
+ return;
+ }
+ if (ShouldFetchDeviceInformation())
+ device_info_handler_->Start();
+}
+
+void ChromeToMobileReceiveService::SetDeviceTags(
+ const std::map<std::string, std::string>& printer_tags) {
+ if (!device_info_handler_->HasRequiredInformation(printer_tags)) {
+ OnFailToFetchDeviceInfo();
+ return;
+ }
+
+ printer_tags_ = printer_tags;
+ UpdateServiceObservers();
+ if (IsServiceReadyToBeStarted()) {
+ StartDeviceAndFetchAll();
+ return;
+ }
+}
+
+bool ChromeToMobileReceiveService::IsServiceReadyToBeStarted() const {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ return IsAuthenticationReady() &&
+ IsDeviceInfoReady() &&
+ IsTurnedOnInPreference();
+}
+
+bool ChromeToMobileReceiveService::ShouldFetchDeviceInformation() const {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ return IsAuthenticationReady() &&
+ IsTurnedOnInPreference() &&
+ !IsDeviceInfoReady();
+}
+
+bool ChromeToMobileReceiveService::IsAuthenticationReady() const {
+ TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
+ if (!token_service || !token_service->HasTokenForService(
+ GaiaConstants::kGaiaOAuth2LoginRefreshToken)) {
+ return false;
+ }
+ std::string token = token_service->GetTokenForService(
+ GaiaConstants::kGaiaOAuth2LoginRefreshToken);
+ if (token.empty()) {
+ NOTREACHED();
+ return false;
+ }
+ return true;
+}
+
+bool ChromeToMobileReceiveService::IsTurnedOnInPreference() const {
+ return profile_->GetPrefs()->GetBoolean(prefs::kChromeToMobileReceiveEnabled);
+}
+
+bool ChromeToMobileReceiveService::IsDeviceInfoReady() const {
+ if (!device_info_handler_->IsSupported()) {
+ LOG(INFO) << "Required device support is not available";
+ return false;
+ }
+ return device_info_handler_->HasRequiredInformation(printer_tags_);
+}
+
+bool ChromeToMobileReceiveService::CanGetStartedIfTurnedOn() const {
+ // Once the authentication is ready and the device supports receiving
+ // chrome-to-mobile jobs, the app can start fetching the device information
+ // and starts the device to receive chrome-to-mobile jobs when the user turns
+ // on chrome-to-mobile in preference.
+ return IsAuthenticationReady() &&
+ device_info_handler_->IsSupported();
+}
+
+// A |ChromeToMobileReceiveService| object's status is changed when it calls
+// |backend_| operations or when the callbacks defined in
+// |chrome_to_mobile_receive::ChromeToMobileReceiveFrontend| are called back by
+// |backend_|. Relevant backend operations are:
+// - |backend_->StartDevice()|,
+// - |backend_->StopDevice()|,
+// - |backend_->FetchJobs()|, and
+// - |backend_->CancelAllPendingOperations()|.
+// See |chrome_to_mobile_receive::ChromeToMobileReceiveFrontend| for the
+// callbacks these operations will trigger.
+//
+// The transition of the status and the applicable backend operations in each
+// status are given here:
+// OnCancelAllPendingOperationsComplete()
+// ----------------- OnStopDeviceComplete()
+// | kNotStarted | <-----------------------------
+// ----------------- |
+// |/|\ |
+// | | |
+// | | |
+// backend_->StartDevice()| |OnStartDeviceComplete() -------------------
+// | |(with success == false) | kStopping |
+// | | -------------------
+// | | /|\ /|
+// \|/| | |
+// --------------- backend_->StopDevice() | |
+// | kStarting |---------------------------/ |
+// --------------- backend_->CancelAllPendingOperations() |
+// | |
+// OnStartDeviceComplete() | |
+// (with success = true) | |
+// \|/ |
+// --------------- |
+// | kStarted |---------------------------------------/
+// --------------- backend_->StopDevice()
+// | /|\ backend_->CancelAllPendingOperations()
+// ------
+// backend_->FetchJobs() and its callbackes.
+void ChromeToMobileReceiveService::StartDevice() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ if (!backend_->IsRunning())
+ backend_->Start();
+
+ switch (status_) {
+ case kNotStarted: {
+ std::string printer_id = std::string();
+ PrefService* pref_service = profile_->GetPrefs();
+ if (pref_service->HasPrefPath(prefs::kChromeToMobileReceivePrinterId)) {
+ printer_id = pref_service->GetString(
+ prefs::kChromeToMobileReceivePrinterId);
+ }
+ backend_->StartDevice(printer_id, printer_tags_);
+ status_ = kStarting;
+ break;
+ }
+
+ case kStarting:
+ case kStarted: {
+ // No action if it has been started or it is starting.
+ break;
+ }
+
+ case kStopping: {
+ // If it is stopping, schedule to restart it when it is stopped.
+ should_start_device_when_not_started_ = true;
+ break;
+ }
+
+ default:
+ NOTREACHED();
+ }
+}
+
+void ChromeToMobileReceiveService::FetchJobs() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ switch (status_) {
+ case kNotStarted: {
+ LOG(ERROR) << "Not applicable";
+ break;
+ }
+
+ case kStarting: {
+ should_fetch_jobs_when_started_ = true;
+ break;
+ }
+
+ case kStarted: {
+ should_fetch_jobs_when_started_ = false;
+ backend_->FetchJobs(printer_id_);
+ break;
+ }
+
+ case kStopping: {
+ if (should_start_device_when_not_started_)
+ should_fetch_jobs_when_started_ = true;
+ else
+ LOG(ERROR) << "Not applicable";
+ break;
+ }
+
+ default:
+ NOTREACHED();
+ }
+}
+
+void ChromeToMobileReceiveService::StopDevice() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ should_start_device_when_not_started_ = false;
+ should_fetch_jobs_when_started_ = false;
+
+ switch (status_) {
+ case kNotStarted: {
+ // No action needed.
+ break;
+ }
+
+ case kStarting:
+ case kStarted: {
+ backend_->StopDevice(printer_id_);
+ status_ = kStopping;
+ break;
+ }
+
+ case kStopping: {
+ // No action needed.
+ break;
+ }
+
+ default:
+ NOTREACHED();
+ }
+}
+
+void ChromeToMobileReceiveService::CancelAllPendingOperations() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ should_start_device_when_not_started_ = false;
+ should_fetch_jobs_when_started_ = false;
+
+ switch (status_) {
+ case kNotStarted: {
+ // No action needed.
+ break;
+ }
+
+ case kStarting:
+ case kStarted: {
+ backend_->CancelAllPendingOperations();
+ status_ = kStopping;
+ break;
+ }
+
+ case kStopping: {
+ // No action needed.
+ break;
+ }
+
+ default:
+ NOTREACHED();
+ }
+}
+
+void ChromeToMobileReceiveService::OnStartDeviceComplete(
+ bool success,
+ const std::string printer_id) {
+ // The |status_| was set to |kStarting| in |StartDevice()| which triggers this
+ // callback. Only |backend_->StopDevice()| and
+ // |backend_->CancelAllPendingOperations()| are allowed in |kStarting| status,
+ // which will change the status to be |kStopping|. If this is the case, no
+ // action should be taken here.
+ if (status_ != kStarting)
+ return;
+
+ if (!success || printer_id.empty()) {
+ SetStatusNotStarted();
+ TurnOffPreferenceDueToFailure(kStartDeviceFailure);
+ return;
+ }
+
+ printer_id_ = printer_id;
+ profile_->GetPrefs()->SetString(
+ prefs::kChromeToMobileReceivePrinterId, printer_id);
+ failure_type_ = kNoFailure;
+ SetStatusStarted();
+ UpdateServiceObservers();
+}
+
+void ChromeToMobileReceiveService::OnStopDeviceComplete(bool success) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ printer_id_ = std::string();
+ profile_->GetPrefs()->ClearPref(prefs::kChromeToMobileReceivePrinterId);
+ SetStatusNotStarted();
+ // Also shuts down the backend if the user has signed out.
+ if (!IsAuthenticationReady()) {
+ backend_->ShutDown();
+ }
+}
+
+void ChromeToMobileReceiveService::OnCancelAllPendingOperationsComplete() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ SetStatusNotStarted();
+}
+
+void ChromeToMobileReceiveService::SetStatusStarted() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ status_ = kStarted;
+ // Perform actions scheduled for status |kStarted|.
+ if (should_fetch_jobs_when_started_) {
+ should_fetch_jobs_when_started_ = false;
+ FetchJobs();
+ }
+}
+
+void ChromeToMobileReceiveService::SetStatusNotStarted() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ status_ = kNotStarted;
+ // Perform actions scheduled for status |kNotStarted|.
+ if (should_start_device_when_not_started_) {
+ should_start_device_when_not_started_ = false;
+ StartDevice();
+ }
+}
+
+bool ChromeToMobileReceiveService::FetchPendingJobs() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ if (ShouldFetchDeviceInformation()) {
+ device_info_handler_->Start();
+ return true;
+ }
+ if (!IsServiceReadyToBeStarted())
+ return false;
+ StartDeviceAndFetchAll();
+ return true;
+}
+
+void ChromeToMobileReceiveService::StartDeviceAndFetchAll() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ DCHECK(IsServiceReadyToBeStarted());
+ StartDevice();
+ FetchJobs();
+}
+
+void ChromeToMobileReceiveService::OnCloudPrintAuthError() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ CancelAllPendingOperations();
+ TurnOffPreferenceDueToFailure(kClouPrintAuthError);
+}
+
+void ChromeToMobileReceiveService::OnOAuth2AccessTokenFetchError() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ CancelAllPendingOperations();
+ TurnOffPreferenceDueToFailure(kOAuthTokenFetchFailure);
+}
+
+void ChromeToMobileReceiveService::OnFailToFetchDeviceInfo() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ // Ignore failure if device information is already available.
+ if (IsDeviceInfoReady())
+ return;
+ // Note the service has not been started due to device information missing.
+ TurnOffPreferenceDueToFailure(kDeviceInfoFetchFailure);
+}
+
+void ChromeToMobileReceiveService::TurnOffPreferenceDueToFailure(
+ FailureType failure_type) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ if (!IsTurnedOnInPreference())
+ return;
+ LOG(ERROR) << "Turn off chrome2mobile due to failure " << failure_type;
+ failure_type_ = failure_type;
+ profile_->GetPrefs()->SetBoolean(prefs::kChromeToMobileReceiveEnabled, false);
+ profile_->GetPrefs()->SetBoolean(
+ prefs::kChromeToMobileReceiveStartingSuppressed, true);
+ UpdateServiceObservers();
+}
+
+ChromeToMobileReceiveService::FailureType
+ ChromeToMobileReceiveService::GetFailureType() const {
+ return failure_type_;
+}
+
+void ChromeToMobileReceiveService::UpdateServiceObservers() {
+ FOR_EACH_OBSERVER(ChromeToMobileReceiveServiceObserver,
+ service_observers_, OnStateChange());
+}
+
+ObserverList<ChromeToMobileReceiveJobObserver>*
+ ChromeToMobileReceiveService::GetJobObservers() {
+ return &job_observers_;
+}
+
+void ChromeToMobileReceiveService::AddJobObserver(
+ ChromeToMobileReceiveJobObserver* observer) {
+ job_observers_.AddObserver(observer);
+}
+
+void ChromeToMobileReceiveService::RemoveJobObserver(
+ ChromeToMobileReceiveJobObserver* observer) {
+ job_observers_.RemoveObserver(observer);
+}
+
+void ChromeToMobileReceiveService::AddServiceObserver(
+ ChromeToMobileReceiveServiceObserver* observer) {
+ service_observers_.AddObserver(observer);
+}
+
+void ChromeToMobileReceiveService::RemoveServiceObserver(
+ ChromeToMobileReceiveServiceObserver* observer) {
+ service_observers_.RemoveObserver(observer);
+}

Powered by Google App Engine
This is Rietveld 408576698