sync_worker.cc revision 010d83a9304c5a91596085d917d248abff47903a
1// Copyright 2014 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/sync_file_system/drive_backend/sync_worker.h"
6
7#include <vector>
8
9#include "base/bind.h"
10#include "base/memory/weak_ptr.h"
11#include "base/threading/sequenced_worker_pool.h"
12#include "base/values.h"
13#include "chrome/browser/drive/drive_api_service.h"
14#include "chrome/browser/drive/drive_notification_manager.h"
15#include "chrome/browser/drive/drive_notification_manager_factory.h"
16#include "chrome/browser/drive/drive_service_interface.h"
17#include "chrome/browser/drive/drive_uploader.h"
18#include "chrome/browser/extensions/extension_service.h"
19#include "chrome/browser/profiles/profile.h"
20#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
21#include "chrome/browser/signin/signin_manager_factory.h"
22#include "chrome/browser/sync_file_system/drive_backend/callback_helper.h"
23#include "chrome/browser/sync_file_system/drive_backend/conflict_resolver.h"
24#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
25#include "chrome/browser/sync_file_system/drive_backend/list_changes_task.h"
26#include "chrome/browser/sync_file_system/drive_backend/local_to_remote_syncer.h"
27#include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
28#include "chrome/browser/sync_file_system/drive_backend/register_app_task.h"
29#include "chrome/browser/sync_file_system/drive_backend/remote_change_processor_on_worker.h"
30#include "chrome/browser/sync_file_system/drive_backend/remote_change_processor_wrapper.h"
31#include "chrome/browser/sync_file_system/drive_backend/remote_to_local_syncer.h"
32#include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
33#include "chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.h"
34#include "chrome/browser/sync_file_system/drive_backend/sync_task.h"
35#include "chrome/browser/sync_file_system/drive_backend/uninstall_app_task.h"
36#include "chrome/browser/sync_file_system/file_status_observer.h"
37#include "chrome/browser/sync_file_system/logger.h"
38#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
39#include "components/signin/core/browser/profile_oauth2_token_service.h"
40#include "components/signin/core/browser/signin_manager.h"
41#include "content/public/browser/browser_thread.h"
42#include "extensions/browser/extension_system.h"
43#include "extensions/browser/extension_system_provider.h"
44#include "extensions/browser/extensions_browser_client.h"
45#include "extensions/common/extension.h"
46#include "google_apis/drive/drive_api_url_generator.h"
47#include "google_apis/drive/gdata_wapi_url_generator.h"
48#include "webkit/common/blob/scoped_file.h"
49#include "webkit/common/fileapi/file_system_util.h"
50
51namespace sync_file_system {
52
53class RemoteChangeProcessor;
54
55namespace drive_backend {
56
57namespace {
58
59void EmptyStatusCallback(SyncStatusCode status) {}
60
61void QueryAppStatusOnUIThread(
62    const base::WeakPtr<ExtensionServiceInterface>& extension_service_ptr,
63    const std::vector<std::string>* app_ids,
64    SyncWorker::AppStatusMap* status,
65    const base::Closure& callback) {
66  ExtensionServiceInterface* extension_service = extension_service_ptr.get();
67  if (!extension_service) {
68    callback.Run();
69    return;
70  }
71
72  for (std::vector<std::string>::const_iterator itr = app_ids->begin();
73       itr != app_ids->end(); ++itr) {
74    const std::string& app_id = *itr;
75    if (!extension_service->GetInstalledExtension(app_id))
76      (*status)[app_id] = SyncWorker::APP_STATUS_UNINSTALLED;
77    else if (!extension_service->IsExtensionEnabled(app_id))
78      (*status)[app_id] = SyncWorker::APP_STATUS_DISABLED;
79    else
80      (*status)[app_id] = SyncWorker::APP_STATUS_ENABLED;
81  }
82
83  callback.Run();
84}
85
86}  // namespace
87
88scoped_ptr<SyncWorker> SyncWorker::CreateOnWorker(
89    const base::FilePath& base_dir,
90    Observer* observer,
91    const base::WeakPtr<ExtensionServiceInterface>& extension_service,
92    scoped_ptr<SyncEngineContext> sync_engine_context,
93    leveldb::Env* env_override) {
94  scoped_ptr<SyncWorker> sync_worker(
95      new SyncWorker(base_dir,
96                     extension_service,
97                     sync_engine_context.Pass(),
98                     env_override));
99  sync_worker->AddObserver(observer);
100  sync_worker->Initialize();
101
102  return sync_worker.Pass();
103}
104
105SyncWorker::~SyncWorker() {}
106
107void SyncWorker::Initialize() {
108  DCHECK(!task_manager_);
109
110  task_manager_.reset(new SyncTaskManager(
111      weak_ptr_factory_.GetWeakPtr(), 0 /* maximum_background_task */));
112  task_manager_->Initialize(SYNC_STATUS_OK);
113
114  PostInitializeTask();
115
116  net::NetworkChangeNotifier::ConnectionType type =
117      net::NetworkChangeNotifier::GetConnectionType();
118  network_available_ =
119      type != net::NetworkChangeNotifier::CONNECTION_NONE;
120}
121
122void SyncWorker::RegisterOrigin(
123    const GURL& origin,
124    const SyncStatusCallback& callback) {
125  if (!GetMetadataDatabase() && GetDriveService()->HasRefreshToken())
126    PostInitializeTask();
127
128  scoped_ptr<RegisterAppTask> task(
129      new RegisterAppTask(context_.get(), origin.host()));
130  if (task->CanFinishImmediately()) {
131    context_->GetUITaskRunner()->PostTask(
132        FROM_HERE, base::Bind(callback, SYNC_STATUS_OK));
133    return;
134  }
135
136  // TODO(peria): Forward |callback| to UI thread.
137  task_manager_->ScheduleSyncTask(
138      FROM_HERE,
139      task.PassAs<SyncTask>(),
140      SyncTaskManager::PRIORITY_HIGH,
141      callback);
142}
143
144void SyncWorker::EnableOrigin(
145    const GURL& origin,
146    const SyncStatusCallback& callback) {
147  // TODO(peria): Forward |callback| to UI thread.
148  task_manager_->ScheduleTask(
149      FROM_HERE,
150      base::Bind(&SyncWorker::DoEnableApp,
151                 weak_ptr_factory_.GetWeakPtr(),
152                 origin.host()),
153      SyncTaskManager::PRIORITY_HIGH,
154      callback);
155}
156
157void SyncWorker::DisableOrigin(
158    const GURL& origin,
159    const SyncStatusCallback& callback) {
160  // TODO(peria): Forward |callback| to UI thread.
161  task_manager_->ScheduleTask(
162      FROM_HERE,
163      base::Bind(&SyncWorker::DoDisableApp,
164                 weak_ptr_factory_.GetWeakPtr(),
165                 origin.host()),
166      SyncTaskManager::PRIORITY_HIGH,
167      callback);
168}
169
170void SyncWorker::UninstallOrigin(
171    const GURL& origin,
172    RemoteFileSyncService::UninstallFlag flag,
173    const SyncStatusCallback& callback) {
174  // TODO(peria): Forward |callback| to UI thread.
175  task_manager_->ScheduleSyncTask(
176      FROM_HERE,
177      scoped_ptr<SyncTask>(
178          new UninstallAppTask(context_.get(), origin.host(), flag)),
179      SyncTaskManager::PRIORITY_HIGH,
180      callback);
181}
182
183void SyncWorker::ProcessRemoteChange(
184    const SyncFileCallback& callback) {
185  RemoteToLocalSyncer* syncer = new RemoteToLocalSyncer(context_.get());
186  task_manager_->ScheduleSyncTask(
187      FROM_HERE,
188      scoped_ptr<SyncTask>(syncer),
189      SyncTaskManager::PRIORITY_MED,
190      base::Bind(&SyncWorker::DidProcessRemoteChange,
191                 weak_ptr_factory_.GetWeakPtr(),
192                 syncer, callback));
193}
194
195void SyncWorker::SetRemoteChangeProcessor(
196    RemoteChangeProcessorOnWorker* remote_change_processor_on_worker) {
197  context_->SetRemoteChangeProcessor(remote_change_processor_on_worker);
198}
199
200RemoteServiceState SyncWorker::GetCurrentState() const {
201  if (!sync_enabled_)
202    return REMOTE_SERVICE_DISABLED;
203  return service_state_;
204}
205
206void SyncWorker::GetOriginStatusMap(
207    RemoteFileSyncService::OriginStatusMap* status_map) {
208  DCHECK(status_map);
209
210  if (!GetMetadataDatabase())
211    return;
212
213  std::vector<std::string> app_ids;
214  GetMetadataDatabase()->GetRegisteredAppIDs(&app_ids);
215
216  for (std::vector<std::string>::const_iterator itr = app_ids.begin();
217       itr != app_ids.end(); ++itr) {
218    const std::string& app_id = *itr;
219    GURL origin =
220        extensions::Extension::GetBaseURLFromExtensionId(app_id);
221    (*status_map)[origin] =
222        GetMetadataDatabase()->IsAppEnabled(app_id) ?
223        "Enabled" : "Disabled";
224  }
225}
226
227scoped_ptr<base::ListValue> SyncWorker::DumpFiles(const GURL& origin) {
228  if (!GetMetadataDatabase())
229    return scoped_ptr<base::ListValue>();
230  return GetMetadataDatabase()->DumpFiles(origin.host());
231}
232
233scoped_ptr<base::ListValue> SyncWorker::DumpDatabase() {
234  if (!GetMetadataDatabase())
235    return scoped_ptr<base::ListValue>();
236  return GetMetadataDatabase()->DumpDatabase();
237}
238
239void SyncWorker::SetSyncEnabled(bool enabled) {
240  if (sync_enabled_ == enabled)
241    return;
242
243  RemoteServiceState old_state = GetCurrentState();
244  sync_enabled_ = enabled;
245  if (old_state == GetCurrentState())
246    return;
247
248  FOR_EACH_OBSERVER(
249      Observer, observers_,
250      UpdateServiceState(
251          GetCurrentState(),
252          enabled ? "Sync is enabled" : "Sync is disabled"));
253}
254
255SyncStatusCode SyncWorker::SetDefaultConflictResolutionPolicy(
256    ConflictResolutionPolicy policy) {
257  default_conflict_resolution_policy_ = policy;
258  return SYNC_STATUS_OK;
259}
260
261SyncStatusCode SyncWorker::SetConflictResolutionPolicy(
262    const GURL& origin,
263    ConflictResolutionPolicy policy) {
264  NOTIMPLEMENTED();
265  default_conflict_resolution_policy_ = policy;
266  return SYNC_STATUS_OK;
267}
268
269ConflictResolutionPolicy SyncWorker::GetDefaultConflictResolutionPolicy()
270    const {
271  return default_conflict_resolution_policy_;
272}
273
274ConflictResolutionPolicy SyncWorker::GetConflictResolutionPolicy(
275    const GURL& origin) const {
276  NOTIMPLEMENTED();
277  return default_conflict_resolution_policy_;
278}
279
280void SyncWorker::ApplyLocalChange(
281    const FileChange& local_change,
282    const base::FilePath& local_path,
283    const SyncFileMetadata& local_metadata,
284    const fileapi::FileSystemURL& url,
285    const SyncStatusCallback& callback) {
286  LocalToRemoteSyncer* syncer = new LocalToRemoteSyncer(
287      context_.get(), local_metadata, local_change, local_path, url);
288  task_manager_->ScheduleSyncTask(
289      FROM_HERE,
290      scoped_ptr<SyncTask>(syncer),
291      SyncTaskManager::PRIORITY_MED,
292      base::Bind(&SyncWorker::DidApplyLocalChange,
293                 weak_ptr_factory_.GetWeakPtr(),
294                 syncer, callback));
295}
296
297void SyncWorker::MaybeScheduleNextTask() {
298  if (GetCurrentState() == REMOTE_SERVICE_DISABLED)
299    return;
300
301  // TODO(tzik): Notify observer of OnRemoteChangeQueueUpdated.
302  // TODO(tzik): Add an interface to get the number of dirty trackers to
303  // MetadataDatabase.
304
305  MaybeStartFetchChanges();
306}
307
308void SyncWorker::NotifyLastOperationStatus(
309    SyncStatusCode status,
310    bool used_network) {
311  UpdateServiceStateFromSyncStatusCode(status, used_network);
312
313  if (GetMetadataDatabase()) {
314    FOR_EACH_OBSERVER(
315        Observer, observers_,
316        OnPendingFileListUpdated(GetMetadataDatabase()->CountDirtyTracker()));
317  }
318}
319
320void SyncWorker::OnNotificationReceived() {
321  if (service_state_ == REMOTE_SERVICE_TEMPORARY_UNAVAILABLE)
322    UpdateServiceState(REMOTE_SERVICE_OK, "Got push notification for Drive.");
323
324  should_check_remote_change_ = true;
325  MaybeScheduleNextTask();
326}
327
328void SyncWorker::OnReadyToSendRequests(const std::string& account_id) {
329  if (service_state_ == REMOTE_SERVICE_OK)
330    return;
331  UpdateServiceState(REMOTE_SERVICE_OK, "Authenticated");
332
333  if (!GetMetadataDatabase() && !account_id.empty()) {
334    GetDriveService()->Initialize(account_id);
335    PostInitializeTask();
336    return;
337  }
338
339  should_check_remote_change_ = true;
340  MaybeScheduleNextTask();
341}
342
343void SyncWorker::OnRefreshTokenInvalid() {
344  UpdateServiceState(
345      REMOTE_SERVICE_AUTHENTICATION_REQUIRED,
346      "Found invalid refresh token.");
347}
348
349void SyncWorker::OnNetworkChanged(
350    net::NetworkChangeNotifier::ConnectionType type) {
351  bool new_network_availability =
352      type != net::NetworkChangeNotifier::CONNECTION_NONE;
353
354  if (network_available_ && !new_network_availability) {
355    UpdateServiceState(REMOTE_SERVICE_TEMPORARY_UNAVAILABLE, "Disconnected");
356  } else if (!network_available_ && new_network_availability) {
357    UpdateServiceState(REMOTE_SERVICE_OK, "Connected");
358    should_check_remote_change_ = true;
359    MaybeStartFetchChanges();
360  }
361  network_available_ = new_network_availability;
362}
363
364drive::DriveServiceInterface* SyncWorker::GetDriveService() {
365  return context_->GetDriveService();
366}
367
368drive::DriveUploaderInterface* SyncWorker::GetDriveUploader() {
369  return context_->GetDriveUploader();
370}
371
372MetadataDatabase* SyncWorker::GetMetadataDatabase() {
373  return context_->GetMetadataDatabase();
374}
375
376SyncTaskManager* SyncWorker::GetSyncTaskManager() {
377  return task_manager_.get();
378}
379
380void SyncWorker::AddObserver(Observer* observer) {
381  observers_.AddObserver(observer);
382}
383
384SyncWorker::SyncWorker(
385    const base::FilePath& base_dir,
386    const base::WeakPtr<ExtensionServiceInterface>& extension_service,
387    scoped_ptr<SyncEngineContext> sync_engine_context,
388    leveldb::Env* env_override)
389    : base_dir_(base_dir),
390      env_override_(env_override),
391      service_state_(REMOTE_SERVICE_TEMPORARY_UNAVAILABLE),
392      should_check_conflict_(true),
393      should_check_remote_change_(true),
394      listing_remote_changes_(false),
395      sync_enabled_(false),
396      default_conflict_resolution_policy_(
397          CONFLICT_RESOLUTION_POLICY_LAST_WRITE_WIN),
398      network_available_(false),
399      extension_service_(extension_service),
400      context_(sync_engine_context.Pass()),
401      weak_ptr_factory_(this) {}
402
403void SyncWorker::DoDisableApp(const std::string& app_id,
404                              const SyncStatusCallback& callback) {
405  if (GetMetadataDatabase()) {
406    GetMetadataDatabase()->DisableApp(app_id, callback);
407  } else {
408    context_->GetUITaskRunner()->PostTask(
409        FROM_HERE, base::Bind(callback, SYNC_STATUS_OK));
410  }
411}
412
413void SyncWorker::DoEnableApp(const std::string& app_id,
414                             const SyncStatusCallback& callback) {
415  if (GetMetadataDatabase()) {
416    GetMetadataDatabase()->EnableApp(app_id, callback);
417  } else {
418    context_->GetUITaskRunner()->PostTask(
419        FROM_HERE, base::Bind(callback, SYNC_STATUS_OK));
420  }
421}
422
423void SyncWorker::PostInitializeTask() {
424  DCHECK(!GetMetadataDatabase());
425
426  // This initializer task may not run if MetadataDatabase in context_ is
427  // already initialized when it runs.
428  SyncEngineInitializer* initializer =
429      new SyncEngineInitializer(context_.get(),
430                                context_->GetFileTaskRunner(),
431                                base_dir_.Append(kDatabaseName),
432                                env_override_);
433  task_manager_->ScheduleSyncTask(
434      FROM_HERE,
435      scoped_ptr<SyncTask>(initializer),
436      SyncTaskManager::PRIORITY_HIGH,
437      base::Bind(&SyncWorker::DidInitialize,
438                 weak_ptr_factory_.GetWeakPtr(),
439                 initializer));
440}
441
442void SyncWorker::DidInitialize(SyncEngineInitializer* initializer,
443                               SyncStatusCode status) {
444  if (status != SYNC_STATUS_OK) {
445    if (GetDriveService()->HasRefreshToken()) {
446      UpdateServiceState(REMOTE_SERVICE_TEMPORARY_UNAVAILABLE,
447                         "Could not initialize remote service");
448    } else {
449      UpdateServiceState(REMOTE_SERVICE_AUTHENTICATION_REQUIRED,
450                         "Authentication required.");
451    }
452    return;
453  }
454
455  scoped_ptr<MetadataDatabase> metadata_database =
456      initializer->PassMetadataDatabase();
457  if (metadata_database)
458    context_->SetMetadataDatabase(metadata_database.Pass());
459
460  UpdateRegisteredApp();
461}
462
463void SyncWorker::UpdateRegisteredApp() {
464  MetadataDatabase* metadata_db = GetMetadataDatabase();
465  DCHECK(metadata_db);
466
467  scoped_ptr<std::vector<std::string> > app_ids(new std::vector<std::string>);
468  metadata_db->GetRegisteredAppIDs(app_ids.get());
469
470  AppStatusMap* app_status = new AppStatusMap;
471  base::Closure callback =
472      base::Bind(&SyncWorker::DidQueryAppStatus,
473                 weak_ptr_factory_.GetWeakPtr(),
474                 base::Owned(app_status));
475
476  context_->GetUITaskRunner()->PostTask(
477      FROM_HERE,
478      base::Bind(&QueryAppStatusOnUIThread,
479                 extension_service_,
480                 base::Owned(app_ids.release()),
481                 app_status,
482                 RelayCallbackToTaskRunner(
483                     context_->GetWorkerTaskRunner(),
484                     FROM_HERE, callback)));
485}
486
487void SyncWorker::DidQueryAppStatus(const AppStatusMap* app_status) {
488  MetadataDatabase* metadata_db = GetMetadataDatabase();
489  DCHECK(metadata_db);
490
491  // Update the status of every origin using status from ExtensionService.
492  for (AppStatusMap::const_iterator itr = app_status->begin();
493       itr != app_status->end(); ++itr) {
494    const std::string& app_id = itr->first;
495    GURL origin = extensions::Extension::GetBaseURLFromExtensionId(app_id);
496
497    if (itr->second == APP_STATUS_UNINSTALLED) {
498      // Extension has been uninstalled.
499      // (At this stage we can't know if it was unpacked extension or not,
500      // so just purge the remote folder.)
501      UninstallOrigin(origin,
502                      RemoteFileSyncService::UNINSTALL_AND_PURGE_REMOTE,
503                      base::Bind(&EmptyStatusCallback));
504      continue;
505    }
506
507    FileTracker tracker;
508    if (!metadata_db->FindAppRootTracker(app_id, &tracker)) {
509      // App will register itself on first run.
510      continue;
511    }
512
513    DCHECK(itr->second == APP_STATUS_ENABLED ||
514           itr->second == APP_STATUS_DISABLED);
515    bool is_app_enabled = (itr->second == APP_STATUS_ENABLED);
516    bool is_app_root_tracker_enabled =
517        (tracker.tracker_kind() == TRACKER_KIND_APP_ROOT);
518    if (is_app_enabled && !is_app_root_tracker_enabled)
519      EnableOrigin(origin, base::Bind(&EmptyStatusCallback));
520    else if (!is_app_enabled && is_app_root_tracker_enabled)
521      DisableOrigin(origin, base::Bind(&EmptyStatusCallback));
522  }
523}
524
525void SyncWorker::DidProcessRemoteChange(RemoteToLocalSyncer* syncer,
526                                        const SyncFileCallback& callback,
527                                        SyncStatusCode status) {
528  if (syncer->is_sync_root_deletion()) {
529    MetadataDatabase::ClearDatabase(context_->PassMetadataDatabase());
530    PostInitializeTask();
531    callback.Run(status, syncer->url());
532    return;
533  }
534
535  if (status == SYNC_STATUS_OK) {
536    if (syncer->sync_action() != SYNC_ACTION_NONE &&
537        syncer->url().is_valid()) {
538      FOR_EACH_OBSERVER(
539          Observer, observers_,
540          OnFileStatusChanged(
541              syncer->url(),
542              SYNC_FILE_STATUS_SYNCED,
543              syncer->sync_action(),
544              SYNC_DIRECTION_REMOTE_TO_LOCAL));
545    }
546
547    if (syncer->sync_action() == SYNC_ACTION_DELETED &&
548        syncer->url().is_valid() &&
549        fileapi::VirtualPath::IsRootPath(syncer->url().path())) {
550      RegisterOrigin(syncer->url().origin(), base::Bind(&EmptyStatusCallback));
551    }
552    should_check_conflict_ = true;
553  }
554  callback.Run(status, syncer->url());
555}
556
557void SyncWorker::DidApplyLocalChange(LocalToRemoteSyncer* syncer,
558                                     const SyncStatusCallback& callback,
559                                     SyncStatusCode status) {
560  if ((status == SYNC_STATUS_OK || status == SYNC_STATUS_RETRY) &&
561      syncer->url().is_valid() &&
562      syncer->sync_action() != SYNC_ACTION_NONE) {
563    fileapi::FileSystemURL updated_url = syncer->url();
564    if (!syncer->target_path().empty()) {
565      updated_url = CreateSyncableFileSystemURL(syncer->url().origin(),
566                                                syncer->target_path());
567    }
568    FOR_EACH_OBSERVER(Observer, observers_,
569                      OnFileStatusChanged(updated_url,
570                                          SYNC_FILE_STATUS_SYNCED,
571                                          syncer->sync_action(),
572                                          SYNC_DIRECTION_LOCAL_TO_REMOTE));
573  }
574
575  if (status == SYNC_STATUS_UNKNOWN_ORIGIN && syncer->url().is_valid()) {
576    RegisterOrigin(syncer->url().origin(),
577                   base::Bind(&EmptyStatusCallback));
578  }
579
580  if (syncer->needs_remote_change_listing() &&
581      !listing_remote_changes_) {
582    task_manager_->ScheduleSyncTask(
583        FROM_HERE,
584        scoped_ptr<SyncTask>(new ListChangesTask(context_.get())),
585        SyncTaskManager::PRIORITY_HIGH,
586        base::Bind(&SyncWorker::DidFetchChanges,
587                   weak_ptr_factory_.GetWeakPtr()));
588    should_check_remote_change_ = false;
589    listing_remote_changes_ = true;
590    time_to_check_changes_ =
591        base::TimeTicks::Now() +
592        base::TimeDelta::FromSeconds(kListChangesRetryDelaySeconds);
593  }
594
595  if (status == SYNC_STATUS_OK)
596    should_check_conflict_ = true;
597
598  callback.Run(status);
599}
600
601void SyncWorker::MaybeStartFetchChanges() {
602  if (GetCurrentState() == REMOTE_SERVICE_DISABLED)
603    return;
604
605  if (!GetMetadataDatabase())
606    return;
607
608  if (listing_remote_changes_)
609    return;
610
611  base::TimeTicks now = base::TimeTicks::Now();
612  if (!should_check_remote_change_ && now < time_to_check_changes_) {
613    if (!GetMetadataDatabase()->HasDirtyTracker() &&
614        should_check_conflict_) {
615      should_check_conflict_ = false;
616      task_manager_->ScheduleSyncTaskIfIdle(
617          FROM_HERE,
618          scoped_ptr<SyncTask>(new ConflictResolver(context_.get())),
619          base::Bind(&SyncWorker::DidResolveConflict,
620                     weak_ptr_factory_.GetWeakPtr()));
621    }
622    return;
623  }
624
625  if (task_manager_->ScheduleSyncTaskIfIdle(
626          FROM_HERE,
627          scoped_ptr<SyncTask>(new ListChangesTask(context_.get())),
628          base::Bind(&SyncWorker::DidFetchChanges,
629                     weak_ptr_factory_.GetWeakPtr()))) {
630    should_check_remote_change_ = false;
631    listing_remote_changes_ = true;
632    time_to_check_changes_ =
633        now + base::TimeDelta::FromSeconds(kListChangesRetryDelaySeconds);
634  }
635}
636
637void SyncWorker::DidResolveConflict(SyncStatusCode status) {
638  if (status == SYNC_STATUS_OK)
639    should_check_conflict_ = true;
640}
641
642void SyncWorker::DidFetchChanges(SyncStatusCode status) {
643  if (status == SYNC_STATUS_OK)
644    should_check_conflict_ = true;
645  listing_remote_changes_ = false;
646}
647
648void SyncWorker::UpdateServiceStateFromSyncStatusCode(
649    SyncStatusCode status,
650    bool used_network) {
651  switch (status) {
652    case SYNC_STATUS_OK:
653      if (used_network)
654        UpdateServiceState(REMOTE_SERVICE_OK, std::string());
655      break;
656
657    // Authentication error.
658    case SYNC_STATUS_AUTHENTICATION_FAILED:
659      UpdateServiceState(REMOTE_SERVICE_AUTHENTICATION_REQUIRED,
660                         "Authentication required");
661      break;
662
663    // OAuth token error.
664    case SYNC_STATUS_ACCESS_FORBIDDEN:
665      UpdateServiceState(REMOTE_SERVICE_AUTHENTICATION_REQUIRED,
666                         "Access forbidden");
667      break;
668
669    // Errors which could make the service temporarily unavailable.
670    case SYNC_STATUS_SERVICE_TEMPORARILY_UNAVAILABLE:
671    case SYNC_STATUS_NETWORK_ERROR:
672    case SYNC_STATUS_ABORT:
673    case SYNC_STATUS_FAILED:
674      if (GetDriveService()->HasRefreshToken()) {
675        UpdateServiceState(REMOTE_SERVICE_TEMPORARY_UNAVAILABLE,
676                           "Network or temporary service error.");
677      } else {
678        UpdateServiceState(REMOTE_SERVICE_AUTHENTICATION_REQUIRED,
679                           "Authentication required");
680      }
681      break;
682
683    // Errors which would require manual user intervention to resolve.
684    case SYNC_DATABASE_ERROR_CORRUPTION:
685    case SYNC_DATABASE_ERROR_IO_ERROR:
686    case SYNC_DATABASE_ERROR_FAILED:
687      UpdateServiceState(REMOTE_SERVICE_DISABLED,
688                         "Unrecoverable database error");
689      break;
690
691    default:
692      // Other errors don't affect service state
693      break;
694  }
695}
696
697void SyncWorker::UpdateServiceState(RemoteServiceState state,
698                                    const std::string& description) {
699  RemoteServiceState old_state = GetCurrentState();
700  service_state_ = state;
701
702  if (old_state == GetCurrentState())
703    return;
704
705  util::Log(logging::LOG_VERBOSE, FROM_HERE,
706            "Service state changed: %d->%d: %s",
707            old_state, GetCurrentState(), description.c_str());
708
709  FOR_EACH_OBSERVER(
710      Observer, observers_,
711      UpdateServiceState(GetCurrentState(), description));
712}
713
714}  // namespace drive_backend
715}  // namespace sync_file_system
716