sync_file_system_service.cc revision 868fa2fe829687343ffae624259930155e16dbd8
14a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project// Copyright (c) 2012 The Chromium Authors. All rights reserved.
24a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project// Use of this source code is governed by a BSD-style license that can be
34a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project// found in the LICENSE file.
44a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project
54a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "chrome/browser/sync_file_system/sync_file_system_service.h"
64a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project
74a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include <string>
84a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project
94a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "base/bind.h"
104a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "base/logging.h"
114a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "base/memory/ref_counted.h"
124a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "base/stl_util.h"
134a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "chrome/browser/extensions/api/sync_file_system/extension_sync_event_observer.h"
144a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "chrome/browser/profiles/profile.h"
154a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "chrome/browser/sync/profile_sync_service.h"
164a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "chrome/browser/sync/profile_sync_service_factory.h"
174a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "chrome/browser/sync_file_system/drive_file_sync_service.h"
184a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "chrome/browser/sync_file_system/local_file_sync_service.h"
194a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "chrome/browser/sync_file_system/logger.h"
204a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "chrome/browser/sync_file_system/sync_event_observer.h"
214a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "chrome/common/chrome_notification_types.h"
224a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "chrome/common/extensions/extension.h"
234a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
244a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "content/public/browser/browser_thread.h"
254a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "content/public/browser/notification_details.h"
264a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "content/public/browser/notification_service.h"
274a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "googleurl/src/gurl.h"
284a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "webkit/browser/fileapi/file_system_context.h"
294a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "webkit/browser/fileapi/syncable/sync_direction.h"
304a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "webkit/browser/fileapi/syncable/sync_file_metadata.h"
314a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project#include "webkit/browser/fileapi/syncable/sync_status_code.h"
324a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project
334a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Projectusing content::BrowserThread;
344a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Projectusing fileapi::FileSystemURL;
354a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Projectusing fileapi::FileSystemURLSet;
364a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project
374a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Projectnamespace sync_file_system {
384a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project
394a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Projectnamespace {
404a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project
414a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Projectconst int64 kRetryTimerIntervalInSeconds = 20 * 60;  // 20 min.
424a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project
434a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source ProjectSyncServiceState RemoteStateToSyncServiceState(
444a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project    RemoteServiceState state) {
454a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project  switch (state) {
464a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project    case REMOTE_SERVICE_OK:
474a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project      return SYNC_SERVICE_RUNNING;
484a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project    case REMOTE_SERVICE_TEMPORARY_UNAVAILABLE:
494a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project      return SYNC_SERVICE_TEMPORARY_UNAVAILABLE;
504a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project    case REMOTE_SERVICE_AUTHENTICATION_REQUIRED:
514a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project      return SYNC_SERVICE_AUTHENTICATION_REQUIRED;
524a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project    case REMOTE_SERVICE_DISABLED:
534a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project      return SYNC_SERVICE_DISABLED;
544a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project  }
554a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project  NOTREACHED() << "Unknown remote service state: " << state;
564a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project  return SYNC_SERVICE_DISABLED;
574a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project}
584a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project
594a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Projectvoid DidHandleOriginForExtensionUnloadedEvent(
604a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project    int type,
614a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project    extension_misc::UnloadedExtensionReason reason,
624a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project    const GURL& origin,
634a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project    SyncStatusCode code) {
644a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project  DCHECK(chrome::NOTIFICATION_EXTENSION_UNLOADED == type);
654a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project  DCHECK(extension_misc::UNLOAD_REASON_DISABLE == reason ||
664a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project         extension_misc::UNLOAD_REASON_UNINSTALL == reason);
674a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project  if (code != SYNC_STATUS_OK &&
684a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project      code != SYNC_STATUS_UNKNOWN_ORIGIN) {
694a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project    switch (reason) {
704a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project      case extension_misc::UNLOAD_REASON_DISABLE:
714a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project        util::Log(logging::LOG_WARNING,
724a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project                  FROM_HERE,
734a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project                  "Disabling origin for UNLOAD(DISABLE) failed: %s",
744a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project                  origin.spec().c_str());
754a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project        break;
764a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project      case extension_misc::UNLOAD_REASON_UNINSTALL:
774a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project        util::Log(logging::LOG_WARNING,
784a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project                  FROM_HERE,
794a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project                  "Uninstall origin for UNLOAD(UNINSTALL) failed: %s",
804a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project                  origin.spec().c_str());
814a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project        break;
824a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project      default:
834a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project        break;
844a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project    }
854a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project  }
864a68b3365c8c50aa93505e99ead2565ab73dcdb0The Android Open Source Project}
87
88void DidHandleOriginForExtensionEnabledEvent(
89    int type,
90    const GURL& origin,
91    SyncStatusCode code) {
92  DCHECK(chrome::NOTIFICATION_EXTENSION_ENABLED == type);
93  if (code != SYNC_STATUS_OK)
94    util::Log(logging::LOG_WARNING,
95              FROM_HERE,
96              "Enabling origin for ENABLED failed: %s",
97              origin.spec().c_str());
98}
99
100}  // namespace
101
102void SyncFileSystemService::Shutdown() {
103  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
104
105  local_file_service_->Shutdown();
106  local_file_service_.reset();
107
108  remote_file_service_.reset();
109
110  ProfileSyncServiceBase* profile_sync_service =
111      ProfileSyncServiceFactory::GetForProfile(profile_);
112  if (profile_sync_service)
113    profile_sync_service->RemoveObserver(this);
114
115  profile_ = NULL;
116}
117
118SyncFileSystemService::~SyncFileSystemService() {
119  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
120  DCHECK(!profile_);
121}
122
123void SyncFileSystemService::InitializeForApp(
124    fileapi::FileSystemContext* file_system_context,
125    const GURL& app_origin,
126    const SyncStatusCallback& callback) {
127  DCHECK(local_file_service_);
128  DCHECK(remote_file_service_);
129  DCHECK(app_origin == app_origin.GetOrigin());
130
131  DVLOG(1) << "InitializeForApp: " << app_origin.spec();
132
133  local_file_service_->MaybeInitializeFileSystemContext(
134      app_origin, file_system_context,
135      base::Bind(&SyncFileSystemService::DidInitializeFileSystem,
136                 AsWeakPtr(), app_origin, callback));
137}
138
139SyncServiceState SyncFileSystemService::GetSyncServiceState() {
140  return RemoteStateToSyncServiceState(remote_file_service_->GetCurrentState());
141}
142
143void SyncFileSystemService::GetExtensionStatusMap(
144    std::map<GURL, std::string>* status_map) {
145  DCHECK(status_map);
146  remote_file_service_->GetOriginStatusMap(status_map);
147}
148
149void SyncFileSystemService::GetFileSyncStatus(
150    const FileSystemURL& url, const SyncFileStatusCallback& callback) {
151  DCHECK(local_file_service_);
152  DCHECK(remote_file_service_);
153
154  // It's possible to get an invalid FileEntry.
155  if (!url.is_valid()) {
156    base::MessageLoopProxy::current()->PostTask(
157        FROM_HERE,
158        base::Bind(callback,
159                   SYNC_FILE_ERROR_INVALID_URL,
160                   SYNC_FILE_STATUS_UNKNOWN));
161    return;
162  }
163
164  if (remote_file_service_->IsConflicting(url)) {
165    base::MessageLoopProxy::current()->PostTask(
166        FROM_HERE,
167        base::Bind(callback,
168                   SYNC_STATUS_OK,
169                   SYNC_FILE_STATUS_CONFLICTING));
170    return;
171  }
172
173  local_file_service_->HasPendingLocalChanges(
174      url,
175      base::Bind(&SyncFileSystemService::DidGetLocalChangeStatus,
176                 AsWeakPtr(), callback));
177}
178
179void SyncFileSystemService::AddSyncEventObserver(SyncEventObserver* observer) {
180  observers_.AddObserver(observer);
181}
182
183void SyncFileSystemService::RemoveSyncEventObserver(
184    SyncEventObserver* observer) {
185  observers_.RemoveObserver(observer);
186}
187
188ConflictResolutionPolicy
189SyncFileSystemService::GetConflictResolutionPolicy() const {
190  return remote_file_service_->GetConflictResolutionPolicy();
191}
192
193SyncStatusCode SyncFileSystemService::SetConflictResolutionPolicy(
194    ConflictResolutionPolicy policy) {
195  return remote_file_service_->SetConflictResolutionPolicy(policy);
196}
197
198SyncFileSystemService::SyncFileSystemService(Profile* profile)
199    : profile_(profile),
200      pending_local_changes_(0),
201      pending_remote_changes_(0),
202      local_sync_running_(false),
203      remote_sync_running_(false),
204      is_waiting_remote_sync_enabled_(false),
205      sync_enabled_(true) {
206}
207
208void SyncFileSystemService::Initialize(
209    scoped_ptr<LocalFileSyncService> local_file_service,
210    scoped_ptr<RemoteFileSyncService> remote_file_service) {
211  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
212  DCHECK(local_file_service);
213  DCHECK(remote_file_service);
214  DCHECK(profile_);
215
216  local_file_service_ = local_file_service.Pass();
217  remote_file_service_ = remote_file_service.Pass();
218
219  local_file_service_->AddChangeObserver(this);
220  local_file_service_->SetLocalChangeProcessor(
221      remote_file_service_->GetLocalChangeProcessor());
222
223  remote_file_service_->AddServiceObserver(this);
224  remote_file_service_->AddFileStatusObserver(this);
225  remote_file_service_->SetRemoteChangeProcessor(local_file_service_.get());
226
227  ProfileSyncServiceBase* profile_sync_service =
228      ProfileSyncServiceFactory::GetForProfile(profile_);
229  if (profile_sync_service) {
230    UpdateSyncEnabledStatus(profile_sync_service);
231    profile_sync_service->AddObserver(this);
232  }
233
234  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
235                 content::Source<Profile>(profile_));
236  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
237                 content::Source<Profile>(profile_));
238  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_ENABLED,
239                 content::Source<Profile>(profile_));
240}
241
242void SyncFileSystemService::DidInitializeFileSystem(
243    const GURL& app_origin,
244    const SyncStatusCallback& callback,
245    SyncStatusCode status) {
246  DVLOG(1) << "DidInitializeFileSystem: "
247           << app_origin.spec() << " " << status;
248
249  if (status != SYNC_STATUS_OK) {
250    callback.Run(status);
251    return;
252  }
253
254  // Local side of initialization for the app is done.
255  // Continue on initializing the remote side.
256  remote_file_service_->RegisterOriginForTrackingChanges(
257      app_origin,
258      base::Bind(&SyncFileSystemService::DidRegisterOrigin,
259                 AsWeakPtr(), app_origin, callback));
260}
261
262void SyncFileSystemService::DidRegisterOrigin(
263    const GURL& app_origin,
264    const SyncStatusCallback& callback,
265    SyncStatusCode status) {
266  DVLOG(1) << "DidRegisterOrigin: " << app_origin.spec() << " " << status;
267
268  callback.Run(status);
269}
270
271void SyncFileSystemService::SetSyncEnabledForTesting(bool enabled) {
272  sync_enabled_ = enabled;
273  remote_file_service_->SetSyncEnabled(sync_enabled_);
274}
275
276void SyncFileSystemService::MaybeStartSync() {
277  if (!profile_ || !sync_enabled_)
278    return;
279
280  if (pending_local_changes_ + pending_remote_changes_ == 0)
281    return;
282
283  DVLOG(2) << "MaybeStartSync() called (remote service state:"
284           << remote_file_service_->GetCurrentState() << ")";
285  switch (remote_file_service_->GetCurrentState()) {
286    case REMOTE_SERVICE_OK:
287      break;
288
289    case REMOTE_SERVICE_TEMPORARY_UNAVAILABLE:
290      if (sync_retry_timer_.IsRunning())
291        return;
292      sync_retry_timer_.Start(
293          FROM_HERE,
294          base::TimeDelta::FromSeconds(kRetryTimerIntervalInSeconds),
295          this, &SyncFileSystemService::MaybeStartSync);
296      break;
297
298    case REMOTE_SERVICE_AUTHENTICATION_REQUIRED:
299    case REMOTE_SERVICE_DISABLED:
300      // No point to run sync.
301      return;
302  }
303
304  StartRemoteSync();
305  StartLocalSync();
306}
307
308void SyncFileSystemService::StartRemoteSync() {
309  // See if we cannot / should not start a new remote sync.
310  if (remote_sync_running_ || pending_remote_changes_ == 0)
311    return;
312  // If we have registered a URL for waiting until sync is enabled on a
313  // file (and the registerred URL seems to be still valid) it won't be
314  // worth trying to start another remote sync.
315  if (is_waiting_remote_sync_enabled_)
316    return;
317  DCHECK(sync_enabled_);
318
319  DVLOG(1) << "Calling ProcessRemoteChange";
320  remote_sync_running_ = true;
321  remote_file_service_->ProcessRemoteChange(
322      base::Bind(&SyncFileSystemService::DidProcessRemoteChange,
323                 AsWeakPtr()));
324}
325
326void SyncFileSystemService::StartLocalSync() {
327  // See if we cannot / should not start a new local sync.
328  if (local_sync_running_ || pending_local_changes_ == 0)
329    return;
330  DCHECK(sync_enabled_);
331
332  DVLOG(1) << "Calling ProcessLocalChange";
333  local_sync_running_ = true;
334  local_file_service_->ProcessLocalChange(
335      base::Bind(&SyncFileSystemService::DidProcessLocalChange,
336                 AsWeakPtr()));
337}
338
339void SyncFileSystemService::DidProcessRemoteChange(
340    SyncStatusCode status,
341    const FileSystemURL& url) {
342  DVLOG(1) << "DidProcessRemoteChange: "
343           << " status=" << status
344           << " (" << SyncStatusCodeToString(status) << ")"
345           << " url=" << url.DebugString();
346  DCHECK(remote_sync_running_);
347  remote_sync_running_ = false;
348
349  if (status != SYNC_STATUS_NO_CHANGE_TO_SYNC &&
350      remote_file_service_->GetCurrentState() != REMOTE_SERVICE_DISABLED) {
351    DCHECK(url.is_valid());
352    local_file_service_->ClearSyncFlagForURL(url);
353  }
354
355  if (status == SYNC_STATUS_NO_CHANGE_TO_SYNC) {
356    // We seem to have no changes to work on for now.
357    // TODO(kinuko): Might be better setting a timer to call MaybeStartSync.
358    return;
359  }
360  if (status == SYNC_STATUS_FILE_BUSY) {
361    is_waiting_remote_sync_enabled_ = true;
362    local_file_service_->RegisterURLForWaitingSync(
363        url, base::Bind(&SyncFileSystemService::OnSyncEnabledForRemoteSync,
364                        AsWeakPtr()));
365    return;
366  }
367
368  base::MessageLoopProxy::current()->PostTask(
369      FROM_HERE, base::Bind(&SyncFileSystemService::MaybeStartSync,
370                            AsWeakPtr()));
371}
372
373void SyncFileSystemService::DidProcessLocalChange(
374    SyncStatusCode status, const FileSystemURL& url) {
375  DVLOG(1) << "DidProcessLocalChange:"
376           << " status=" << status
377           << " (" << SyncStatusCodeToString(status) << ")"
378           << " url=" << url.DebugString();
379  DCHECK(local_sync_running_);
380  local_sync_running_ = false;
381
382  if (status == SYNC_STATUS_NO_CHANGE_TO_SYNC) {
383    // We seem to have no changes to work on for now.
384    return;
385  }
386
387  DCHECK(url.is_valid());
388  local_file_service_->ClearSyncFlagForURL(url);
389
390  base::MessageLoopProxy::current()->PostTask(
391      FROM_HERE, base::Bind(&SyncFileSystemService::MaybeStartSync,
392                            AsWeakPtr()));
393}
394
395void SyncFileSystemService::DidGetLocalChangeStatus(
396    const SyncFileStatusCallback& callback,
397    SyncStatusCode status,
398    bool has_pending_local_changes) {
399  callback.Run(
400      status,
401      has_pending_local_changes ?
402          SYNC_FILE_STATUS_HAS_PENDING_CHANGES : SYNC_FILE_STATUS_SYNCED);
403}
404
405void SyncFileSystemService::OnSyncEnabledForRemoteSync() {
406  is_waiting_remote_sync_enabled_ = false;
407  MaybeStartSync();
408}
409
410void SyncFileSystemService::OnLocalChangeAvailable(int64 pending_changes) {
411  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
412  DCHECK_GE(pending_changes, 0);
413  DVLOG(1) << "OnLocalChangeAvailable: " << pending_changes;
414  pending_local_changes_ = pending_changes;
415  if (pending_changes == 0)
416    return;
417
418  base::MessageLoopProxy::current()->PostTask(
419      FROM_HERE, base::Bind(&SyncFileSystemService::MaybeStartSync,
420                            AsWeakPtr()));
421}
422
423void SyncFileSystemService::OnRemoteChangeQueueUpdated(int64 pending_changes) {
424  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
425  DCHECK_GE(pending_changes, 0);
426  DVLOG(1) << "OnRemoteChangeQueueUpdated: " << pending_changes;
427  pending_remote_changes_ = pending_changes;
428  if (pending_changes == 0)
429    return;
430
431  // The smallest change available might have changed from the previous one.
432  // Reset the is_waiting_remote_sync_enabled_ flag so that we can retry.
433  is_waiting_remote_sync_enabled_ = false;
434
435  base::MessageLoopProxy::current()->PostTask(
436      FROM_HERE, base::Bind(&SyncFileSystemService::MaybeStartSync,
437                            AsWeakPtr()));
438}
439
440void SyncFileSystemService::OnRemoteServiceStateUpdated(
441    RemoteServiceState state,
442    const std::string& description) {
443  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
444  DVLOG(1) << "OnRemoteServiceStateUpdated: " << state
445           << " " << description;
446
447  if (state == REMOTE_SERVICE_OK) {
448    base::MessageLoopProxy::current()->PostTask(
449        FROM_HERE, base::Bind(&SyncFileSystemService::MaybeStartSync,
450                              AsWeakPtr()));
451  }
452
453  FOR_EACH_OBSERVER(
454      SyncEventObserver, observers_,
455      OnSyncStateUpdated(GURL(),
456                         RemoteStateToSyncServiceState(state),
457                         description));
458}
459
460void SyncFileSystemService::Observe(
461    int type,
462    const content::NotificationSource& source,
463    const content::NotificationDetails& details) {
464  // Event notification sequence.
465  //
466  // (User action)    (Notification type)
467  // Install:         INSTALLED.
468  // Update:          INSTALLED.
469  // Uninstall:       UNLOADED(UNINSTALL).
470  // Launch, Close:   No notification.
471  // Enable:          ENABLED.
472  // Disable:         UNLOADED(DISABLE).
473  // Reload, Restart: UNLOADED(DISABLE) -> INSTALLED -> ENABLED.
474  //
475  switch (type) {
476    case chrome::NOTIFICATION_EXTENSION_INSTALLED:
477      HandleExtensionInstalled(details);
478      break;
479    case chrome::NOTIFICATION_EXTENSION_UNLOADED:
480      HandleExtensionUnloaded(type, details);
481      break;
482    case chrome::NOTIFICATION_EXTENSION_ENABLED:
483      HandleExtensionEnabled(type, details);
484      break;
485    default:
486      NOTREACHED() << "Unknown notification.";
487      break;
488  }
489}
490
491void SyncFileSystemService::HandleExtensionInstalled(
492    const content::NotificationDetails& details) {
493  const extensions::Extension* extension =
494      content::Details<const extensions::InstalledExtensionInfo>(details)->
495          extension;
496  GURL app_origin =
497      extensions::Extension::GetBaseURLFromExtensionId(extension->id());
498  DVLOG(1) << "Handle extension notification for INSTALLED: " << app_origin;
499  // NOTE: When an app is uninstalled and re-installed in a sequence,
500  // |local_file_service_| may still keeps |app_origin| as disabled origin.
501  local_file_service_->SetOriginEnabled(app_origin, true);
502}
503
504void SyncFileSystemService::HandleExtensionUnloaded(
505    int type,
506    const content::NotificationDetails& details) {
507  content::Details<const extensions::UnloadedExtensionInfo> info(details);
508  std::string extension_id = info->extension->id();
509  GURL app_origin =
510      extensions::Extension::GetBaseURLFromExtensionId(extension_id);
511
512  switch (info->reason) {
513    case extension_misc::UNLOAD_REASON_DISABLE:
514      DVLOG(1) << "Handle extension notification for UNLOAD(DISABLE): "
515               << app_origin;
516      remote_file_service_->DisableOriginForTrackingChanges(
517          app_origin,
518          base::Bind(&DidHandleOriginForExtensionUnloadedEvent,
519                     type, info->reason, app_origin));
520      local_file_service_->SetOriginEnabled(app_origin, false);
521      break;
522    case extension_misc::UNLOAD_REASON_UNINSTALL:
523      DVLOG(1) << "Handle extension notification for UNLOAD(UNINSTALL): "
524               << app_origin;
525      remote_file_service_->UninstallOrigin(
526          app_origin,
527          base::Bind(&DidHandleOriginForExtensionUnloadedEvent,
528                     type, info->reason, app_origin));
529      local_file_service_->SetOriginEnabled(app_origin, false);
530      break;
531    default:
532      // Nothing to do.
533      break;
534  }
535}
536
537void SyncFileSystemService::HandleExtensionEnabled(
538    int type,
539    const content::NotificationDetails& details) {
540  std::string extension_id =
541      content::Details<const extensions::Extension>(details)->id();
542  GURL app_origin =
543      extensions::Extension::GetBaseURLFromExtensionId(extension_id);
544  DVLOG(1) << "Handle extension notification for ENABLED: " << app_origin;
545  remote_file_service_->EnableOriginForTrackingChanges(
546      app_origin,
547      base::Bind(&DidHandleOriginForExtensionEnabledEvent, type, app_origin));
548  local_file_service_->SetOriginEnabled(app_origin, true);
549}
550
551void SyncFileSystemService::OnStateChanged() {
552  ProfileSyncServiceBase* profile_sync_service =
553      ProfileSyncServiceFactory::GetForProfile(profile_);
554  if (profile_sync_service)
555    UpdateSyncEnabledStatus(profile_sync_service);
556}
557
558void SyncFileSystemService::OnFileStatusChanged(
559    const FileSystemURL& url,
560    SyncFileStatus sync_status,
561    SyncAction action_taken,
562    SyncDirection direction) {
563  FOR_EACH_OBSERVER(
564      SyncEventObserver, observers_,
565      OnFileSynced(url, sync_status, action_taken, direction));
566}
567
568void SyncFileSystemService::UpdateSyncEnabledStatus(
569    ProfileSyncServiceBase* profile_sync_service) {
570  if (!profile_sync_service->HasSyncSetupCompleted())
571    return;
572  sync_enabled_ = profile_sync_service->GetActiveDataTypes().Has(
573      syncer::APPS);
574  remote_file_service_->SetSyncEnabled(sync_enabled_);
575  if (sync_enabled_) {
576    base::MessageLoopProxy::current()->PostTask(
577        FROM_HERE, base::Bind(&SyncFileSystemService::MaybeStartSync,
578                              AsWeakPtr()));
579  }
580}
581
582}  // namespace sync_file_system
583