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