sync_file_system_service.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/sync_file_system/sync_file_system_service.h"
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/format_macros.h"
11#include "base/logging.h"
12#include "base/memory/ref_counted.h"
13#include "base/stl_util.h"
14#include "chrome/browser/chrome_notification_types.h"
15#include "chrome/browser/extensions/api/sync_file_system/extension_sync_event_observer.h"
16#include "chrome/browser/extensions/api/sync_file_system/sync_file_system_api_helpers.h"
17#include "chrome/browser/extensions/extension_prefs.h"
18#include "chrome/browser/profiles/profile.h"
19#include "chrome/browser/sync/profile_sync_service.h"
20#include "chrome/browser/sync/profile_sync_service_factory.h"
21#include "chrome/browser/sync_file_system/drive_backend_v1/drive_file_sync_service.h"
22#include "chrome/browser/sync_file_system/local/local_file_sync_service.h"
23#include "chrome/browser/sync_file_system/logger.h"
24#include "chrome/browser/sync_file_system/sync_direction.h"
25#include "chrome/browser/sync_file_system/sync_event_observer.h"
26#include "chrome/browser/sync_file_system/sync_file_metadata.h"
27#include "chrome/browser/sync_file_system/sync_status_code.h"
28#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
29#include "chrome/common/extensions/extension.h"
30#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
31#include "content/public/browser/browser_thread.h"
32#include "content/public/browser/notification_details.h"
33#include "content/public/browser/notification_service.h"
34#include "content/public/browser/storage_partition.h"
35#include "url/gurl.h"
36#include "webkit/browser/fileapi/file_system_context.h"
37
38using content::BrowserThread;
39using extensions::Extension;
40using extensions::ExtensionPrefs;
41using fileapi::FileSystemURL;
42using fileapi::FileSystemURLSet;
43
44namespace sync_file_system {
45
46namespace {
47
48const int64 kSyncDelayInSeconds = 1;
49const int64 kSyncDelaySlowInSeconds = 30;  // Start with 30 sec + exp backoff.
50const int64 kSyncDelayMaxInSeconds = 30 * 60;  // 30 min.
51
52SyncServiceState RemoteStateToSyncServiceState(
53    RemoteServiceState state) {
54  switch (state) {
55    case REMOTE_SERVICE_OK:
56      return SYNC_SERVICE_RUNNING;
57    case REMOTE_SERVICE_TEMPORARY_UNAVAILABLE:
58      return SYNC_SERVICE_TEMPORARY_UNAVAILABLE;
59    case REMOTE_SERVICE_AUTHENTICATION_REQUIRED:
60      return SYNC_SERVICE_AUTHENTICATION_REQUIRED;
61    case REMOTE_SERVICE_DISABLED:
62      return SYNC_SERVICE_DISABLED;
63  }
64  NOTREACHED() << "Unknown remote service state: " << state;
65  return SYNC_SERVICE_DISABLED;
66}
67
68void DidHandleOriginForExtensionUnloadedEvent(
69    int type,
70    const GURL& origin,
71    SyncStatusCode code) {
72  DCHECK(chrome::NOTIFICATION_EXTENSION_UNLOADED == type ||
73         chrome::NOTIFICATION_EXTENSION_UNINSTALLED == type);
74  if (code != SYNC_STATUS_OK &&
75      code != SYNC_STATUS_UNKNOWN_ORIGIN) {
76    switch (type) {
77      case chrome::NOTIFICATION_EXTENSION_UNLOADED:
78        util::Log(logging::LOG_WARNING,
79                  FROM_HERE,
80                  "Disabling origin for UNLOADED(DISABLE) failed: %s",
81                  origin.spec().c_str());
82        break;
83      case chrome::NOTIFICATION_EXTENSION_UNINSTALLED:
84        util::Log(logging::LOG_WARNING,
85                  FROM_HERE,
86                  "Uninstall origin for UNINSTALLED failed: %s",
87                  origin.spec().c_str());
88        break;
89      default:
90        break;
91    }
92  }
93}
94
95void DidHandleOriginForExtensionEnabledEvent(
96    int type,
97    const GURL& origin,
98    SyncStatusCode code) {
99  DCHECK(chrome::NOTIFICATION_EXTENSION_ENABLED == type);
100  if (code != SYNC_STATUS_OK)
101    util::Log(logging::LOG_WARNING,
102              FROM_HERE,
103              "Enabling origin for ENABLED failed: %s",
104              origin.spec().c_str());
105}
106
107std::string SyncFileStatusToString(SyncFileStatus sync_file_status) {
108  return extensions::api::sync_file_system::ToString(
109      extensions::SyncFileStatusToExtensionEnum(sync_file_status));
110}
111
112// Gets called repeatedly until every SyncFileStatus has been mapped.
113void DidGetFileSyncStatusForDump(
114    base::ListValue* files,
115    size_t* num_results,
116    const SyncFileSystemService::DumpFilesCallback& callback,
117    base::DictionaryValue* file,
118    SyncStatusCode sync_status_code,
119    SyncFileStatus sync_file_status) {
120  DCHECK(files);
121  DCHECK(num_results);
122
123  if (file)
124    file->SetString("status", SyncFileStatusToString(sync_file_status));
125
126  // Once all results have been received, run the callback to signal end.
127  DCHECK_LE(*num_results, files->GetSize());
128  if (++*num_results < files->GetSize())
129    return;
130
131  callback.Run(files);
132}
133
134}  // namespace
135
136class SyncFileSystemService::SyncRunner {
137 public:
138  typedef base::Callback<void(const SyncStatusCallback&)> Task;
139  SyncRunner(const std::string& task_name,
140             const Task& sync_task,
141             RemoteFileSyncService* remote_service)
142    : task_name_(task_name),
143      sync_task_(sync_task),
144      remote_service_(remote_service),
145      current_delay_(0),
146      last_delay_(0),
147      pending_changes_(0),
148      running_(false),
149      factory_(this) {}
150  ~SyncRunner() {}
151
152  void Schedule() {
153    int64 delay = kSyncDelayInSeconds;
154    if (pending_changes_ == 0) {
155      ScheduleInternal(kSyncDelayMaxInSeconds);
156      return;
157    }
158    switch (remote_service_->GetCurrentState()) {
159      case REMOTE_SERVICE_OK:
160        delay = kSyncDelayInSeconds;
161        break;
162
163      case REMOTE_SERVICE_TEMPORARY_UNAVAILABLE:
164        delay = kSyncDelaySlowInSeconds;
165        if (last_delay_ >= kSyncDelaySlowInSeconds &&
166            last_delay_ < kSyncDelayMaxInSeconds)
167          delay = last_delay_ * 2;
168        break;
169
170      case REMOTE_SERVICE_AUTHENTICATION_REQUIRED:
171      case REMOTE_SERVICE_DISABLED:
172        delay = kSyncDelayMaxInSeconds;
173        break;
174    }
175    ScheduleInternal(delay);
176  }
177
178  void ScheduleIfNotRunning() {
179    if (!timer_.IsRunning())
180      Schedule();
181  }
182
183  void OnChangesUpdated(int64 pending_changes) {
184    DCHECK_GE(pending_changes, 0);
185    if (pending_changes_ != pending_changes) {
186      util::Log(logging::LOG_VERBOSE, FROM_HERE,
187                "[%s] pending_changes updated: %" PRId64,
188                task_name_.c_str(), pending_changes);
189    }
190    pending_changes_ = pending_changes;
191    Schedule();
192  }
193
194  int64 pending_changes() const { return pending_changes_; }
195
196 private:
197  void Finished(SyncStatusCode status) {
198    DCHECK(running_);
199    running_ = false;
200    util::Log(logging::LOG_VERBOSE, FROM_HERE,
201              "[%s] * Finished (elapsed: %" PRId64 " sec)",
202              task_name_.c_str(),
203              (base::Time::Now() - last_scheduled_).InSeconds());
204    if (status == SYNC_STATUS_NO_CHANGE_TO_SYNC ||
205        status == SYNC_STATUS_FILE_BUSY)
206      ScheduleInternal(kSyncDelayMaxInSeconds);
207    else
208      Schedule();
209  }
210
211  void Run() {
212    if (running_)
213      return;
214    running_ = true;
215    last_scheduled_ = base::Time::Now();
216    last_delay_ = current_delay_;
217
218    util::Log(logging::LOG_VERBOSE, FROM_HERE,
219              "[%s] * Started", task_name_.c_str());
220
221    sync_task_.Run(base::Bind(&SyncRunner::Finished, factory_.GetWeakPtr()));
222  }
223
224  void ScheduleInternal(int64 delay) {
225    base::TimeDelta time_to_next = base::TimeDelta::FromSeconds(delay);
226
227    if (timer_.IsRunning()) {
228      if (current_delay_ == delay)
229        return;
230
231      base::TimeDelta elapsed = base::Time::Now() - last_scheduled_;
232      if (elapsed < time_to_next)
233        time_to_next = time_to_next - elapsed;
234      else
235        time_to_next = base::TimeDelta::FromSeconds(kSyncDelayInSeconds);
236      timer_.Stop();
237    }
238
239    if (current_delay_ != delay) {
240      util::Log(logging::LOG_VERBOSE, FROM_HERE,
241                "[%s] Scheduling task in %" PRId64 " secs",
242                task_name_.c_str(), time_to_next.InSeconds());
243    }
244    current_delay_ = delay;
245
246    timer_.Start(FROM_HERE, time_to_next, this, &SyncRunner::Run);
247  }
248
249  std::string task_name_;
250  Task sync_task_;
251  RemoteFileSyncService* remote_service_;
252  base::OneShotTimer<SyncRunner> timer_;
253  base::Time last_scheduled_;
254  int64 current_delay_;
255  int64 last_delay_;
256  int64 pending_changes_;
257  bool running_;
258  base::WeakPtrFactory<SyncRunner> factory_;
259
260  DISALLOW_COPY_AND_ASSIGN(SyncRunner);
261};
262
263//-----------------------------------------------------------------------------
264
265void SyncFileSystemService::Shutdown() {
266  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
267
268  local_sync_.reset();
269  remote_sync_.reset();
270
271  local_file_service_->Shutdown();
272  local_file_service_.reset();
273
274  remote_file_service_.reset();
275
276  ProfileSyncServiceBase* profile_sync_service =
277      ProfileSyncServiceFactory::GetForProfile(profile_);
278  if (profile_sync_service)
279    profile_sync_service->RemoveObserver(this);
280
281  profile_ = NULL;
282}
283
284SyncFileSystemService::~SyncFileSystemService() {
285  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
286  DCHECK(!profile_);
287}
288
289void SyncFileSystemService::InitializeForApp(
290    fileapi::FileSystemContext* file_system_context,
291    const GURL& app_origin,
292    const SyncStatusCallback& callback) {
293  DCHECK(local_file_service_);
294  DCHECK(remote_file_service_);
295  DCHECK(app_origin == app_origin.GetOrigin());
296
297  util::Log(logging::LOG_VERBOSE, FROM_HERE,
298            "Initializing for App: %s", app_origin.spec().c_str());
299
300  local_file_service_->MaybeInitializeFileSystemContext(
301      app_origin, file_system_context,
302      base::Bind(&SyncFileSystemService::DidInitializeFileSystem,
303                 AsWeakPtr(), app_origin, callback));
304}
305
306SyncServiceState SyncFileSystemService::GetSyncServiceState() {
307  return RemoteStateToSyncServiceState(remote_file_service_->GetCurrentState());
308}
309
310void SyncFileSystemService::GetExtensionStatusMap(
311    std::map<GURL, std::string>* status_map) {
312  DCHECK(status_map);
313  remote_file_service_->GetOriginStatusMap(status_map);
314}
315
316void SyncFileSystemService::DumpFiles(const GURL& origin,
317                                      const DumpFilesCallback& callback) {
318  DCHECK(!origin.is_empty());
319
320  content::StoragePartition* storage_partition =
321      content::BrowserContext::GetStoragePartitionForSite(profile_, origin);
322  fileapi::FileSystemContext* file_system_context =
323      storage_partition->GetFileSystemContext();
324  local_file_service_->MaybeInitializeFileSystemContext(
325      origin, file_system_context,
326      base::Bind(&SyncFileSystemService::DidInitializeFileSystemForDump,
327                 AsWeakPtr(), origin, callback));
328}
329
330void SyncFileSystemService::GetFileSyncStatus(
331    const FileSystemURL& url, const SyncFileStatusCallback& callback) {
332  DCHECK(local_file_service_);
333  DCHECK(remote_file_service_);
334
335  // It's possible to get an invalid FileEntry.
336  if (!url.is_valid()) {
337    base::MessageLoopProxy::current()->PostTask(
338        FROM_HERE,
339        base::Bind(callback,
340                   SYNC_FILE_ERROR_INVALID_URL,
341                   SYNC_FILE_STATUS_UNKNOWN));
342    return;
343  }
344
345  if (remote_file_service_->IsConflicting(url)) {
346    base::MessageLoopProxy::current()->PostTask(
347        FROM_HERE,
348        base::Bind(callback,
349                   SYNC_STATUS_OK,
350                   SYNC_FILE_STATUS_CONFLICTING));
351    return;
352  }
353
354  local_file_service_->HasPendingLocalChanges(
355      url,
356      base::Bind(&SyncFileSystemService::DidGetLocalChangeStatus,
357                 AsWeakPtr(), callback));
358}
359
360void SyncFileSystemService::AddSyncEventObserver(SyncEventObserver* observer) {
361  observers_.AddObserver(observer);
362}
363
364void SyncFileSystemService::RemoveSyncEventObserver(
365    SyncEventObserver* observer) {
366  observers_.RemoveObserver(observer);
367}
368
369ConflictResolutionPolicy
370SyncFileSystemService::GetConflictResolutionPolicy() const {
371  return remote_file_service_->GetConflictResolutionPolicy();
372}
373
374SyncStatusCode SyncFileSystemService::SetConflictResolutionPolicy(
375    ConflictResolutionPolicy policy) {
376  return remote_file_service_->SetConflictResolutionPolicy(policy);
377}
378
379SyncFileSystemService::SyncFileSystemService(Profile* profile)
380    : profile_(profile),
381      sync_enabled_(true) {
382}
383
384void SyncFileSystemService::Initialize(
385    scoped_ptr<LocalFileSyncService> local_file_service,
386    scoped_ptr<RemoteFileSyncService> remote_file_service) {
387  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
388  DCHECK(local_file_service);
389  DCHECK(remote_file_service);
390  DCHECK(profile_);
391
392  local_file_service_ = local_file_service.Pass();
393  remote_file_service_ = remote_file_service.Pass();
394
395  local_file_service_->AddChangeObserver(this);
396  local_file_service_->SetLocalChangeProcessor(
397      remote_file_service_->GetLocalChangeProcessor());
398
399  remote_file_service_->AddServiceObserver(this);
400  remote_file_service_->AddFileStatusObserver(this);
401  remote_file_service_->SetRemoteChangeProcessor(local_file_service_.get());
402
403  local_sync_.reset(new SyncRunner(
404      "Local sync",
405      base::Bind(&SyncFileSystemService::StartLocalSync, AsWeakPtr()),
406      remote_file_service_.get()));
407  remote_sync_.reset(new SyncRunner(
408      "Remote sync",
409      base::Bind(&SyncFileSystemService::StartRemoteSync, AsWeakPtr()),
410      remote_file_service_.get()));
411
412  ProfileSyncServiceBase* profile_sync_service =
413      ProfileSyncServiceFactory::GetForProfile(profile_);
414  if (profile_sync_service) {
415    UpdateSyncEnabledStatus(profile_sync_service);
416    profile_sync_service->AddObserver(this);
417  }
418
419  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
420                 content::Source<Profile>(profile_));
421  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
422                 content::Source<Profile>(profile_));
423  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
424                 content::Source<Profile>(profile_));
425  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_ENABLED,
426                 content::Source<Profile>(profile_));
427}
428
429void SyncFileSystemService::DidInitializeFileSystem(
430    const GURL& app_origin,
431    const SyncStatusCallback& callback,
432    SyncStatusCode status) {
433  DVLOG(1) << "DidInitializeFileSystem: "
434           << app_origin.spec() << " " << status;
435
436  if (status != SYNC_STATUS_OK) {
437    callback.Run(status);
438    return;
439  }
440
441  // Local side of initialization for the app is done.
442  // Continue on initializing the remote side.
443  remote_file_service_->RegisterOrigin(
444      app_origin,
445      base::Bind(&SyncFileSystemService::DidRegisterOrigin,
446                 AsWeakPtr(), app_origin, callback));
447}
448
449void SyncFileSystemService::DidRegisterOrigin(
450    const GURL& app_origin,
451    const SyncStatusCallback& callback,
452    SyncStatusCode status) {
453  util::Log(logging::LOG_VERBOSE, FROM_HERE,
454            "DidInitializeForApp (registered the origin): %s: %s",
455            app_origin.spec().c_str(),
456            SyncStatusCodeToString(status));
457
458  callback.Run(status);
459}
460
461void SyncFileSystemService::DidInitializeFileSystemForDump(
462    const GURL& origin,
463    const DumpFilesCallback& callback,
464    SyncStatusCode status) {
465  DCHECK(!origin.is_empty());
466
467  if (status != SYNC_STATUS_OK) {
468    base::ListValue empty_result;
469    callback.Run(&empty_result);
470    return;
471  }
472
473  base::ListValue* files = remote_file_service_->DumpFiles(origin).release();
474  if (!files->GetSize()) {
475    callback.Run(files);
476    return;
477  }
478
479  base::Callback<void(base::DictionaryValue* file,
480                      SyncStatusCode sync_status,
481                      SyncFileStatus sync_file_status)> completion_callback =
482      base::Bind(&DidGetFileSyncStatusForDump, base::Owned(files),
483                 base::Owned(new size_t(0)), callback);
484
485  // After all metadata loaded, sync status can be added to each entry.
486  for (size_t i = 0; i < files->GetSize(); ++i) {
487    base::DictionaryValue* file = NULL;
488    std::string path_string;
489    if (!files->GetDictionary(i, &file) ||
490        !file->GetString("path", &path_string)) {
491      NOTREACHED();
492      completion_callback.Run(
493          NULL, SYNC_FILE_ERROR_FAILED, SYNC_FILE_STATUS_UNKNOWN);
494      continue;
495    }
496
497    base::FilePath file_path = base::FilePath::FromUTF8Unsafe(path_string);
498    FileSystemURL url = CreateSyncableFileSystemURL(origin, file_path);
499    GetFileSyncStatus(url, base::Bind(completion_callback, file));
500  }
501}
502
503void SyncFileSystemService::SetSyncEnabledForTesting(bool enabled) {
504  sync_enabled_ = enabled;
505  remote_file_service_->SetSyncEnabled(sync_enabled_);
506}
507
508void SyncFileSystemService::StartLocalSync(
509    const SyncStatusCallback& callback) {
510  local_file_service_->ProcessLocalChange(
511      base::Bind(&SyncFileSystemService::DidProcessLocalChange, AsWeakPtr(),
512                 callback));
513}
514
515void SyncFileSystemService::StartRemoteSync(
516    const SyncStatusCallback& callback) {
517  remote_file_service_->ProcessRemoteChange(
518      base::Bind(&SyncFileSystemService::DidProcessRemoteChange, AsWeakPtr(),
519                 callback));
520}
521
522void SyncFileSystemService::DidProcessRemoteChange(
523    const SyncStatusCallback& callback,
524    SyncStatusCode status,
525    const FileSystemURL& url) {
526  util::Log(logging::LOG_VERBOSE, FROM_HERE,
527            "ProcessRemoteChange finished with status=%d (%s) for url=%s",
528            status, SyncStatusCodeToString(status), url.DebugString().c_str());
529
530  if (url.is_valid())
531    local_file_service_->ClearSyncFlagForURL(url);
532
533  if (status == SYNC_STATUS_FILE_BUSY) {
534    local_file_service_->RegisterURLForWaitingSync(
535        url, base::Bind(&SyncFileSystemService::OnSyncEnabledForRemoteSync,
536                        AsWeakPtr()));
537  }
538
539  callback.Run(status);
540}
541
542void SyncFileSystemService::DidProcessLocalChange(
543    const SyncStatusCallback& callback,
544    SyncStatusCode status,
545    const FileSystemURL& url) {
546  util::Log(logging::LOG_VERBOSE, FROM_HERE,
547            "ProcessLocalChange finished with status=%d (%s) for url=%s",
548            status, SyncStatusCodeToString(status), url.DebugString().c_str());
549
550  if (url.is_valid())
551    local_file_service_->ClearSyncFlagForURL(url);
552
553  callback.Run(status);
554}
555
556void SyncFileSystemService::DidGetLocalChangeStatus(
557    const SyncFileStatusCallback& callback,
558    SyncStatusCode status,
559    bool has_pending_local_changes) {
560  callback.Run(
561      status,
562      has_pending_local_changes ?
563          SYNC_FILE_STATUS_HAS_PENDING_CHANGES : SYNC_FILE_STATUS_SYNCED);
564}
565
566void SyncFileSystemService::OnSyncEnabledForRemoteSync() {
567  remote_sync_->Schedule();
568}
569
570void SyncFileSystemService::OnLocalChangeAvailable(int64 pending_changes) {
571  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
572
573  local_sync_->OnChangesUpdated(pending_changes);
574  remote_sync_->ScheduleIfNotRunning();
575}
576
577void SyncFileSystemService::OnRemoteChangeQueueUpdated(int64 pending_changes) {
578  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
579
580  remote_sync_->OnChangesUpdated(pending_changes);
581  local_sync_->ScheduleIfNotRunning();
582}
583
584void SyncFileSystemService::OnRemoteServiceStateUpdated(
585    RemoteServiceState state,
586    const std::string& description) {
587  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
588  util::Log(logging::LOG_INFO, FROM_HERE,
589            "OnRemoteServiceStateChanged: %d %s", state, description.c_str());
590
591  remote_sync_->Schedule();
592  local_sync_->Schedule();
593
594  FOR_EACH_OBSERVER(
595      SyncEventObserver, observers_,
596      OnSyncStateUpdated(GURL(),
597                         RemoteStateToSyncServiceState(state),
598                         description));
599}
600
601void SyncFileSystemService::Observe(
602    int type,
603    const content::NotificationSource& source,
604    const content::NotificationDetails& details) {
605  // Event notification sequence.
606  //
607  // (User action)    (Notification type)
608  // Install:         INSTALLED.
609  // Update:          INSTALLED.
610  // Uninstall:       UNINSTALLED.
611  // Launch, Close:   No notification.
612  // Enable:          ENABLED.
613  // Disable:         UNLOADED(DISABLE).
614  // Reload, Restart: UNLOADED(DISABLE) -> INSTALLED -> ENABLED.
615  //
616  switch (type) {
617    case chrome::NOTIFICATION_EXTENSION_INSTALLED:
618      HandleExtensionInstalled(details);
619      break;
620    case chrome::NOTIFICATION_EXTENSION_UNLOADED:
621      HandleExtensionUnloaded(type, details);
622      break;
623    case chrome::NOTIFICATION_EXTENSION_UNINSTALLED:
624      HandleExtensionUninstalled(type, details);
625      break;
626    case chrome::NOTIFICATION_EXTENSION_ENABLED:
627      HandleExtensionEnabled(type, details);
628      break;
629    default:
630      NOTREACHED() << "Unknown notification.";
631      break;
632  }
633}
634
635void SyncFileSystemService::HandleExtensionInstalled(
636    const content::NotificationDetails& details) {
637  const Extension* extension =
638      content::Details<const extensions::InstalledExtensionInfo>(details)->
639          extension;
640  GURL app_origin = Extension::GetBaseURLFromExtensionId(extension->id());
641  DVLOG(1) << "Handle extension notification for INSTALLED: " << app_origin;
642  // NOTE: When an app is uninstalled and re-installed in a sequence,
643  // |local_file_service_| may still keeps |app_origin| as disabled origin.
644  local_file_service_->SetOriginEnabled(app_origin, true);
645}
646
647void SyncFileSystemService::HandleExtensionUnloaded(
648    int type,
649    const content::NotificationDetails& details) {
650  content::Details<const extensions::UnloadedExtensionInfo> info(details);
651  if (info->reason != extension_misc::UNLOAD_REASON_DISABLE)
652    return;
653
654  std::string extension_id = info->extension->id();
655  GURL app_origin = Extension::GetBaseURLFromExtensionId(extension_id);
656
657  int reasons = ExtensionPrefs::Get(profile_)->GetDisableReasons(extension_id);
658  if (reasons & Extension::DISABLE_RELOAD) {
659    // Bypass disabling the origin since the app will be re-enabled soon.
660    // NOTE: If re-enabling the app fails, the app is disabled while it is
661    // handled as enabled origin in the SyncFS. This should be safe and will be
662    // recovered when the user re-enables the app manually or the sync service
663    // restarts.
664    DVLOG(1) << "Handle extension notification for UNLOAD(DISABLE_RELOAD): "
665             << app_origin;
666    return;
667  }
668
669  DVLOG(1) << "Handle extension notification for UNLOAD(DISABLE): "
670           << app_origin;
671  remote_file_service_->DisableOrigin(
672      app_origin,
673      base::Bind(&DidHandleOriginForExtensionUnloadedEvent,
674                 type, app_origin));
675  local_file_service_->SetOriginEnabled(app_origin, false);
676}
677
678void SyncFileSystemService::HandleExtensionUninstalled(
679    int type,
680    const content::NotificationDetails& details) {
681  std::string extension_id = content::Details<const Extension>(details)->id();
682  GURL app_origin = Extension::GetBaseURLFromExtensionId(extension_id);
683  DVLOG(1) << "Handle extension notification for UNINSTALLED: "
684           << app_origin;
685  remote_file_service_->UninstallOrigin(
686      app_origin,
687      base::Bind(&DidHandleOriginForExtensionUnloadedEvent,
688                 type, app_origin));
689  local_file_service_->SetOriginEnabled(app_origin, false);
690}
691
692void SyncFileSystemService::HandleExtensionEnabled(
693    int type,
694    const content::NotificationDetails& details) {
695  std::string extension_id = content::Details<const Extension>(details)->id();
696  GURL app_origin = Extension::GetBaseURLFromExtensionId(extension_id);
697  DVLOG(1) << "Handle extension notification for ENABLED: " << app_origin;
698  remote_file_service_->EnableOrigin(
699      app_origin,
700      base::Bind(&DidHandleOriginForExtensionEnabledEvent, type, app_origin));
701  local_file_service_->SetOriginEnabled(app_origin, true);
702}
703
704void SyncFileSystemService::OnStateChanged() {
705  ProfileSyncServiceBase* profile_sync_service =
706      ProfileSyncServiceFactory::GetForProfile(profile_);
707  if (profile_sync_service)
708    UpdateSyncEnabledStatus(profile_sync_service);
709}
710
711void SyncFileSystemService::OnFileStatusChanged(
712    const FileSystemURL& url,
713    SyncFileStatus sync_status,
714    SyncAction action_taken,
715    SyncDirection direction) {
716  FOR_EACH_OBSERVER(
717      SyncEventObserver, observers_,
718      OnFileSynced(url, sync_status, action_taken, direction));
719}
720
721void SyncFileSystemService::UpdateSyncEnabledStatus(
722    ProfileSyncServiceBase* profile_sync_service) {
723  if (!profile_sync_service->HasSyncSetupCompleted())
724    return;
725  bool old_sync_enabled = sync_enabled_;
726  sync_enabled_ = profile_sync_service->GetActiveDataTypes().Has(
727      syncer::APPS);
728  remote_file_service_->SetSyncEnabled(sync_enabled_);
729  if (!old_sync_enabled && sync_enabled_) {
730    local_sync_->Schedule();
731    remote_sync_->Schedule();
732  }
733}
734
735}  // namespace sync_file_system
736