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); |
+} |