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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 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/browser/chrome_to_mobile/receive/chrome_to_mobile_receive_servi ce.h"
6
7 #include "chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_receive_util. h"
8 #include "chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_receiving_dev ice_info_handler.h"
9 #include "chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_receiving_dev ice_info_handler_ios.h"
10 #include "chrome/browser/prefs/pref_service.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/signin/oauth2_token_service.h"
13 #include "chrome/browser/signin/oauth2_token_service_factory.h"
14 #include "chrome/browser/signin/token_service.h"
15 #include "chrome/browser/signin/token_service_factory.h"
16 #include "chrome/common/chrome_notification_types.h"
17 #include "chrome/common/pref_names.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/notification_service.h"
20 #include "googleurl/src/gurl.h"
21 #include "google_apis/gaia/gaia_constants.h"
22
23 ChromeToMobileReceiveService::ChromeToMobileReceiveService(Profile* profile)
24 : profile_(profile),
25 backend_(new chrome_to_mobile_receive::ChromeToMobileReceiveBackend(
26 this,
27 chrome_to_mobile_receive::GetCloudPrintServerURL(),
28 profile_->GetRequestContext(),
29 OAuth2TokenServiceFactory::GetForProfile(profile_))),
30 #if defined(OS_IOS)
31 device_info_handler_(new chrome_to_mobile_receive::
32 ChromeToMobileReceivingDeviceInfoHandlerIOS()),
33 #else
34 device_info_handler_(new chrome_to_mobile_receive::
35 ChromeToMobileReceivingDeviceInfoHandler()),
36 #endif // defined(OS_IOS)
37 authenticated_(false),
38 failure_type_(kNoFailure),
39 status_(kNotStarted),
40 should_start_device_when_not_started_(false),
41 should_fetch_jobs_when_started_(false) {
42 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
43
44 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
45 registrar_.Add(this,
46 chrome::NOTIFICATION_TOKEN_AVAILABLE,
47 content::Source<TokenService>(token_service));
48 registrar_.Add(this,
49 chrome::NOTIFICATION_GOOGLE_WILL_SIGN_OUT,
50 content::Source<Profile>(profile_));
51
52 PrefService* pref_service = profile_->GetPrefs();
53 if (!pref_service->FindPreference(prefs::kChromeToMobileReceiveEnabled)) {
54 pref_service->RegisterBooleanPref(prefs::kChromeToMobileReceiveEnabled,
55 true,
56 PrefService::UNSYNCABLE_PREF);
57 }
58 if (!pref_service->FindPreference(
59 prefs::kChromeToMobileReceiveStartingSuppressed)) {
60 pref_service->RegisterBooleanPref(
61 prefs::kChromeToMobileReceiveStartingSuppressed,
62 false,
63 PrefService::UNSYNCABLE_PREF);
64 }
65 if (!pref_service->FindPreference(prefs::kChromeToMobileReceivePrinterId)) {
66 pref_service->RegisterStringPref(prefs::kChromeToMobileReceivePrinterId,
67 "",
68 PrefService::UNSYNCABLE_PREF);
69 }
70 }
71
72 ChromeToMobileReceiveService::~ChromeToMobileReceiveService() {
73 registrar_.RemoveAll();
74 }
75
76 void ChromeToMobileReceiveService::Observe(
77 int type,
78 const content::NotificationSource& source,
79 const content::NotificationDetails& details) {
80 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
81
82 switch (type) {
83 case chrome::NOTIFICATION_GOOGLE_WILL_SIGN_OUT: {
84 authenticated_ = false;
85 failure_type_ = kNoFailure;
86 if (status_ == kNotStarted && backend_->IsRunning()) {
87 // Shuts down |backend_|.
88 backend_->ShutDown();
89 return;
90 }
91 // |backend_| will be shut down when |StopDevice()| completes.
92 StopDevice();
93 return;
94 }
95
96 case chrome::NOTIFICATION_TOKEN_AVAILABLE: {
97 const TokenService::TokenAvailableDetails* token_details =
98 content::Details<const TokenService::TokenAvailableDetails>(
99 details).ptr();
100 if (!token_details || token_details->service() !=
101 GaiaConstants::kGaiaOAuth2LoginRefreshToken) {
102 return;
103 }
104
105 DCHECK(IsAuthenticationReady());
106 // Starts the device and fetches jobs if the service is ready.
107 // The if-condition on |authenticated_| is to avoid doing it too often
108 // in the case where multiple authentication notifications are received
109 // without user signing out.
110 if (authenticated_)
111 return;
112
113 authenticated_ = true;
114 if (IsServiceReadyToBeStarted()) {
115 StartDeviceAndFetchAll();
116 } else {
117 // Turns on this device if it was turned off due to failure.
118 PrefService* pref_service = profile_->GetPrefs();
119 if (!pref_service->GetBoolean(prefs::kChromeToMobileReceiveEnabled) &&
120 pref_service->GetBoolean(
121 prefs::kChromeToMobileReceiveStartingSuppressed)) {
122 TurnOnPreference(true);
123 return;
124 }
125 if (ShouldFetchDeviceInformation())
126 device_info_handler_->Start();
127 }
128
129 // Informs service status observers that credientials are updated.
130 UpdateServiceObservers();
131 return;
132 }
133
134 default: {
135 NOTREACHED();
136 }
137 }
138 }
139
140 void ChromeToMobileReceiveService::TurnOnPreference(bool on) {
141 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
142
143 profile_->GetPrefs()->SetBoolean(
144 prefs::kChromeToMobileReceiveStartingSuppressed, false);
145 profile_->GetPrefs()->SetBoolean(prefs::kChromeToMobileReceiveEnabled, on);
146 if (!on) {
147 // Note |failure_type_| is not reset here to surface the error when the
148 // preference is turned off. Note error might be received when stopping
149 // The device.
150 StopDevice();
151 return;
152 }
153
154 failure_type_ = kNoFailure;
155 if (IsServiceReadyToBeStarted()) {
156 StartDeviceAndFetchAll();
157 return;
158 }
159 if (ShouldFetchDeviceInformation())
160 device_info_handler_->Start();
161 }
162
163 void ChromeToMobileReceiveService::SetDeviceTags(
164 const std::map<std::string, std::string>& printer_tags) {
165 if (!device_info_handler_->HasRequiredInformation(printer_tags)) {
166 OnFailToFetchDeviceInfo();
167 return;
168 }
169
170 printer_tags_ = printer_tags;
171 UpdateServiceObservers();
172 if (IsServiceReadyToBeStarted()) {
173 StartDeviceAndFetchAll();
174 return;
175 }
176 }
177
178 bool ChromeToMobileReceiveService::IsServiceReadyToBeStarted() const {
179 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
180 return IsAuthenticationReady() &&
181 IsDeviceInfoReady() &&
182 IsTurnedOnInPreference();
183 }
184
185 bool ChromeToMobileReceiveService::ShouldFetchDeviceInformation() const {
186 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
187 return IsAuthenticationReady() &&
188 IsTurnedOnInPreference() &&
189 !IsDeviceInfoReady();
190 }
191
192 bool ChromeToMobileReceiveService::IsAuthenticationReady() const {
193 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
194 if (!token_service || !token_service->HasTokenForService(
195 GaiaConstants::kGaiaOAuth2LoginRefreshToken)) {
196 return false;
197 }
198 std::string token = token_service->GetTokenForService(
199 GaiaConstants::kGaiaOAuth2LoginRefreshToken);
200 if (token.empty()) {
201 NOTREACHED();
202 return false;
203 }
204 return true;
205 }
206
207 bool ChromeToMobileReceiveService::IsTurnedOnInPreference() const {
208 return profile_->GetPrefs()->GetBoolean(prefs::kChromeToMobileReceiveEnabled);
209 }
210
211 bool ChromeToMobileReceiveService::IsDeviceInfoReady() const {
212 if (!device_info_handler_->IsSupported()) {
213 LOG(INFO) << "Required device support is not available";
214 return false;
215 }
216 return device_info_handler_->HasRequiredInformation(printer_tags_);
217 }
218
219 bool ChromeToMobileReceiveService::CanGetStartedIfTurnedOn() const {
220 // Once the authentication is ready and the device supports receiving
221 // chrome-to-mobile jobs, the app can start fetching the device information
222 // and starts the device to receive chrome-to-mobile jobs when the user turns
223 // on chrome-to-mobile in preference.
224 return IsAuthenticationReady() &&
225 device_info_handler_->IsSupported();
226 }
227
228 // A |ChromeToMobileReceiveService| object's status is changed when it calls
229 // |backend_| operations or when the callbacks defined in
230 // |chrome_to_mobile_receive::ChromeToMobileReceiveFrontend| are called back by
231 // |backend_|. Relevant backend operations are:
232 // - |backend_->StartDevice()|,
233 // - |backend_->StopDevice()|,
234 // - |backend_->FetchJobs()|, and
235 // - |backend_->CancelAllPendingOperations()|.
236 // See |chrome_to_mobile_receive::ChromeToMobileReceiveFrontend| for the
237 // callbacks these operations will trigger.
238 //
239 // The transition of the status and the applicable backend operations in each
240 // status are given here:
241 // OnCancelAllPendingOperationsComplete()
242 // ----------------- OnStopDeviceComplete()
243 // | kNotStarted | <-----------------------------
244 // ----------------- |
245 // |/|\ |
246 // | | |
247 // | | |
248 // backend_->StartDevice()| |OnStartDeviceComplete() -------------------
249 // | |(with success == false) | kStopping |
250 // | | -------------------
251 // | | /|\ /|
252 // \|/| | |
253 // --------------- backend_->StopDevice() | |
254 // | kStarting |---------------------------/ |
255 // --------------- backend_->CancelAllPendingOperations() |
256 // | |
257 // OnStartDeviceComplete() | |
258 // (with success = true) | |
259 // \|/ |
260 // --------------- |
261 // | kStarted |---------------------------------------/
262 // --------------- backend_->StopDevice()
263 // | /|\ backend_->CancelAllPendingOperations()
264 // ------
265 // backend_->FetchJobs() and its callbackes.
266 void ChromeToMobileReceiveService::StartDevice() {
267 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
268
269 if (!backend_->IsRunning())
270 backend_->Start();
271
272 switch (status_) {
273 case kNotStarted: {
274 std::string printer_id = std::string();
275 PrefService* pref_service = profile_->GetPrefs();
276 if (pref_service->HasPrefPath(prefs::kChromeToMobileReceivePrinterId)) {
277 printer_id = pref_service->GetString(
278 prefs::kChromeToMobileReceivePrinterId);
279 }
280 backend_->StartDevice(printer_id, printer_tags_);
281 status_ = kStarting;
282 break;
283 }
284
285 case kStarting:
286 case kStarted: {
287 // No action if it has been started or it is starting.
288 break;
289 }
290
291 case kStopping: {
292 // If it is stopping, schedule to restart it when it is stopped.
293 should_start_device_when_not_started_ = true;
294 break;
295 }
296
297 default:
298 NOTREACHED();
299 }
300 }
301
302 void ChromeToMobileReceiveService::FetchJobs() {
303 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
304
305 switch (status_) {
306 case kNotStarted: {
307 LOG(ERROR) << "Not applicable";
308 break;
309 }
310
311 case kStarting: {
312 should_fetch_jobs_when_started_ = true;
313 break;
314 }
315
316 case kStarted: {
317 should_fetch_jobs_when_started_ = false;
318 backend_->FetchJobs(printer_id_);
319 break;
320 }
321
322 case kStopping: {
323 if (should_start_device_when_not_started_)
324 should_fetch_jobs_when_started_ = true;
325 else
326 LOG(ERROR) << "Not applicable";
327 break;
328 }
329
330 default:
331 NOTREACHED();
332 }
333 }
334
335 void ChromeToMobileReceiveService::StopDevice() {
336 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
337 should_start_device_when_not_started_ = false;
338 should_fetch_jobs_when_started_ = false;
339
340 switch (status_) {
341 case kNotStarted: {
342 // No action needed.
343 break;
344 }
345
346 case kStarting:
347 case kStarted: {
348 backend_->StopDevice(printer_id_);
349 status_ = kStopping;
350 break;
351 }
352
353 case kStopping: {
354 // No action needed.
355 break;
356 }
357
358 default:
359 NOTREACHED();
360 }
361 }
362
363 void ChromeToMobileReceiveService::CancelAllPendingOperations() {
364 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
365
366 should_start_device_when_not_started_ = false;
367 should_fetch_jobs_when_started_ = false;
368
369 switch (status_) {
370 case kNotStarted: {
371 // No action needed.
372 break;
373 }
374
375 case kStarting:
376 case kStarted: {
377 backend_->CancelAllPendingOperations();
378 status_ = kStopping;
379 break;
380 }
381
382 case kStopping: {
383 // No action needed.
384 break;
385 }
386
387 default:
388 NOTREACHED();
389 }
390 }
391
392 void ChromeToMobileReceiveService::OnStartDeviceComplete(
393 bool success,
394 const std::string printer_id) {
395 // The |status_| was set to |kStarting| in |StartDevice()| which triggers this
396 // callback. Only |backend_->StopDevice()| and
397 // |backend_->CancelAllPendingOperations()| are allowed in |kStarting| status,
398 // which will change the status to be |kStopping|. If this is the case, no
399 // action should be taken here.
400 if (status_ != kStarting)
401 return;
402
403 if (!success || printer_id.empty()) {
404 SetStatusNotStarted();
405 TurnOffPreferenceDueToFailure(kStartDeviceFailure);
406 return;
407 }
408
409 printer_id_ = printer_id;
410 profile_->GetPrefs()->SetString(
411 prefs::kChromeToMobileReceivePrinterId, printer_id);
412 failure_type_ = kNoFailure;
413 SetStatusStarted();
414 UpdateServiceObservers();
415 }
416
417 void ChromeToMobileReceiveService::OnStopDeviceComplete(bool success) {
418 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
419
420 printer_id_ = std::string();
421 profile_->GetPrefs()->ClearPref(prefs::kChromeToMobileReceivePrinterId);
422 SetStatusNotStarted();
423 // Also shuts down the backend if the user has signed out.
424 if (!IsAuthenticationReady()) {
425 backend_->ShutDown();
426 }
427 }
428
429 void ChromeToMobileReceiveService::OnCancelAllPendingOperationsComplete() {
430 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
431
432 SetStatusNotStarted();
433 }
434
435 void ChromeToMobileReceiveService::SetStatusStarted() {
436 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
437
438 status_ = kStarted;
439 // Perform actions scheduled for status |kStarted|.
440 if (should_fetch_jobs_when_started_) {
441 should_fetch_jobs_when_started_ = false;
442 FetchJobs();
443 }
444 }
445
446 void ChromeToMobileReceiveService::SetStatusNotStarted() {
447 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
448
449 status_ = kNotStarted;
450 // Perform actions scheduled for status |kNotStarted|.
451 if (should_start_device_when_not_started_) {
452 should_start_device_when_not_started_ = false;
453 StartDevice();
454 }
455 }
456
457 bool ChromeToMobileReceiveService::FetchPendingJobs() {
458 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
459
460 if (ShouldFetchDeviceInformation()) {
461 device_info_handler_->Start();
462 return true;
463 }
464 if (!IsServiceReadyToBeStarted())
465 return false;
466 StartDeviceAndFetchAll();
467 return true;
468 }
469
470 void ChromeToMobileReceiveService::StartDeviceAndFetchAll() {
471 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
472 DCHECK(IsServiceReadyToBeStarted());
473 StartDevice();
474 FetchJobs();
475 }
476
477 void ChromeToMobileReceiveService::OnCloudPrintAuthError() {
478 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
479 CancelAllPendingOperations();
480 TurnOffPreferenceDueToFailure(kClouPrintAuthError);
481 }
482
483 void ChromeToMobileReceiveService::OnOAuth2AccessTokenFetchError() {
484 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
485 CancelAllPendingOperations();
486 TurnOffPreferenceDueToFailure(kOAuthTokenFetchFailure);
487 }
488
489 void ChromeToMobileReceiveService::OnFailToFetchDeviceInfo() {
490 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
491 // Ignore failure if device information is already available.
492 if (IsDeviceInfoReady())
493 return;
494 // Note the service has not been started due to device information missing.
495 TurnOffPreferenceDueToFailure(kDeviceInfoFetchFailure);
496 }
497
498 void ChromeToMobileReceiveService::TurnOffPreferenceDueToFailure(
499 FailureType failure_type) {
500 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
501 if (!IsTurnedOnInPreference())
502 return;
503 LOG(ERROR) << "Turn off chrome2mobile due to failure " << failure_type;
504 failure_type_ = failure_type;
505 profile_->GetPrefs()->SetBoolean(prefs::kChromeToMobileReceiveEnabled, false);
506 profile_->GetPrefs()->SetBoolean(
507 prefs::kChromeToMobileReceiveStartingSuppressed, true);
508 UpdateServiceObservers();
509 }
510
511 ChromeToMobileReceiveService::FailureType
512 ChromeToMobileReceiveService::GetFailureType() const {
513 return failure_type_;
514 }
515
516 void ChromeToMobileReceiveService::UpdateServiceObservers() {
517 FOR_EACH_OBSERVER(ChromeToMobileReceiveServiceObserver,
518 service_observers_, OnStateChange());
519 }
520
521 ObserverList<ChromeToMobileReceiveJobObserver>*
522 ChromeToMobileReceiveService::GetJobObservers() {
523 return &job_observers_;
524 }
525
526 void ChromeToMobileReceiveService::AddJobObserver(
527 ChromeToMobileReceiveJobObserver* observer) {
528 job_observers_.AddObserver(observer);
529 }
530
531 void ChromeToMobileReceiveService::RemoveJobObserver(
532 ChromeToMobileReceiveJobObserver* observer) {
533 job_observers_.RemoveObserver(observer);
534 }
535
536 void ChromeToMobileReceiveService::AddServiceObserver(
537 ChromeToMobileReceiveServiceObserver* observer) {
538 service_observers_.AddObserver(observer);
539 }
540
541 void ChromeToMobileReceiveService::RemoveServiceObserver(
542 ChromeToMobileReceiveServiceObserver* observer) {
543 service_observers_.RemoveObserver(observer);
544 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698