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