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