drive_integration_service.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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/chromeos/drive/drive_integration_service.h"
6
7#include "base/bind.h"
8#include "base/file_util.h"
9#include "base/prefs/pref_change_registrar.h"
10#include "base/prefs/pref_service.h"
11#include "base/strings/stringprintf.h"
12#include "base/threading/sequenced_worker_pool.h"
13#include "chrome/browser/browser_process.h"
14#include "chrome/browser/chromeos/drive/debug_info_collector.h"
15#include "chrome/browser/chromeos/drive/download_handler.h"
16#include "chrome/browser/chromeos/drive/drive_app_registry.h"
17#include "chrome/browser/chromeos/drive/file_cache.h"
18#include "chrome/browser/chromeos/drive/file_system.h"
19#include "chrome/browser/chromeos/drive/file_system_util.h"
20#include "chrome/browser/chromeos/drive/job_scheduler.h"
21#include "chrome/browser/chromeos/drive/logging.h"
22#include "chrome/browser/chromeos/drive/resource_metadata.h"
23#include "chrome/browser/chromeos/drive/resource_metadata_storage.h"
24#include "chrome/browser/chromeos/profiles/profile_util.h"
25#include "chrome/browser/download/download_prefs.h"
26#include "chrome/browser/download/download_service.h"
27#include "chrome/browser/download/download_service_factory.h"
28#include "chrome/browser/drive/drive_api_service.h"
29#include "chrome/browser/drive/drive_api_util.h"
30#include "chrome/browser/drive/drive_notification_manager.h"
31#include "chrome/browser/drive/drive_notification_manager_factory.h"
32#include "chrome/browser/drive/gdata_wapi_service.h"
33#include "chrome/browser/google_apis/auth_service.h"
34#include "chrome/browser/google_apis/gdata_wapi_url_generator.h"
35#include "chrome/browser/profiles/profile.h"
36#include "chrome/browser/signin/profile_oauth2_token_service.h"
37#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
38#include "chrome/common/chrome_version_info.h"
39#include "chrome/common/pref_names.h"
40#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
41#include "content/public/browser/browser_context.h"
42#include "content/public/browser/browser_thread.h"
43#include "webkit/browser/fileapi/external_mount_points.h"
44#include "webkit/common/user_agent/user_agent_util.h"
45
46using content::BrowserContext;
47using content::BrowserThread;
48
49namespace drive {
50namespace {
51
52// Name of the directory used to store metadata.
53const base::FilePath::CharType kMetadataDirectory[] = FILE_PATH_LITERAL("meta");
54
55// Name of the directory used to store cached files.
56const base::FilePath::CharType kCacheFileDirectory[] =
57    FILE_PATH_LITERAL("files");
58
59// Name of the directory used to store temporary files.
60const base::FilePath::CharType kTemporaryFileDirectory[] =
61    FILE_PATH_LITERAL("tmp");
62
63// Returns a user agent string used for communicating with the Drive backend,
64// both WAPI and Drive API.  The user agent looks like:
65//
66// chromedrive-<VERSION> chrome-cc/none (<OS_CPU_INFO>)
67// chromedrive-24.0.1274.0 chrome-cc/none (CrOS x86_64 0.4.0)
68//
69// TODO(satorux): Move this function to somewhere else: crbug.com/151605
70std::string GetDriveUserAgent() {
71  const char kDriveClientName[] = "chromedrive";
72
73  chrome::VersionInfo version_info;
74  const std::string version = (version_info.is_valid() ?
75                               version_info.Version() :
76                               std::string("unknown"));
77
78  // This part is <client_name>/<version>.
79  const char kLibraryInfo[] = "chrome-cc/none";
80
81  const std::string os_cpu_info = webkit_glue::BuildOSCpuInfo();
82
83  // Add "gzip" to receive compressed data from the server.
84  // (see https://developers.google.com/drive/performance)
85  return base::StringPrintf("%s-%s %s (%s) (gzip)",
86                            kDriveClientName,
87                            version.c_str(),
88                            kLibraryInfo,
89                            os_cpu_info.c_str());
90}
91
92// Initializes FileCache and ResourceMetadata.
93// Must be run on the same task runner used by |cache| and |resource_metadata|.
94FileError InitializeMetadata(
95    const base::FilePath& cache_root_directory,
96    internal::ResourceMetadataStorage* metadata_storage,
97    internal::FileCache* cache,
98    internal::ResourceMetadata* resource_metadata) {
99  if (!file_util::CreateDirectory(cache_root_directory.Append(
100          kMetadataDirectory)) ||
101      !file_util::CreateDirectory(cache_root_directory.Append(
102          kCacheFileDirectory)) ||
103      !file_util::CreateDirectory(cache_root_directory.Append(
104          kTemporaryFileDirectory))) {
105    LOG(WARNING) << "Failed to create directories.";
106    return FILE_ERROR_FAILED;
107  }
108
109  // Change permissions of cache file directory to u+rwx,og+x (711) in order to
110  // allow archive files in that directory to be mounted by cros-disks.
111  file_util::SetPosixFilePermissions(
112      cache_root_directory.Append(kCacheFileDirectory),
113      file_util::FILE_PERMISSION_USER_MASK |
114      file_util::FILE_PERMISSION_EXECUTE_BY_GROUP |
115      file_util::FILE_PERMISSION_EXECUTE_BY_OTHERS);
116
117  util::MigrateCacheFilesFromOldDirectories(cache_root_directory,
118                                            kCacheFileDirectory);
119
120  if (!metadata_storage->Initialize()) {
121    LOG(WARNING) << "Failed to initialize the metadata storage.";
122    return FILE_ERROR_FAILED;
123  }
124
125  if (!cache->Initialize()) {
126    LOG(WARNING) << "Failed to initialize the cache.";
127    return FILE_ERROR_FAILED;
128  }
129
130  FileError error = resource_metadata->Initialize();
131  LOG_IF(WARNING, error != FILE_ERROR_OK)
132      << "Failed to initialize resource metadata. " << FileErrorToString(error);
133  return error;
134}
135
136}  // namespace
137
138// Observes drive disable Preference's change.
139class DriveIntegrationService::PreferenceWatcher {
140 public:
141  explicit PreferenceWatcher(PrefService* pref_service)
142      : pref_service_(pref_service),
143        integration_service_(NULL),
144        weak_ptr_factory_(this) {
145    DCHECK(pref_service);
146    pref_change_registrar_.Init(pref_service);
147    pref_change_registrar_.Add(
148        prefs::kDisableDrive,
149        base::Bind(&PreferenceWatcher::OnPreferenceChanged,
150                   weak_ptr_factory_.GetWeakPtr()));
151  }
152
153  void set_integration_service(DriveIntegrationService* integration_service) {
154    integration_service_ = integration_service;
155  }
156
157 private:
158  void OnPreferenceChanged() {
159    DCHECK(integration_service_);
160    integration_service_->SetEnabled(
161        !pref_service_->GetBoolean(prefs::kDisableDrive));
162  }
163
164  PrefService* pref_service_;
165  PrefChangeRegistrar pref_change_registrar_;
166  DriveIntegrationService* integration_service_;
167
168  base::WeakPtrFactory<PreferenceWatcher> weak_ptr_factory_;
169  DISALLOW_COPY_AND_ASSIGN(PreferenceWatcher);
170};
171
172DriveIntegrationService::DriveIntegrationService(
173    Profile* profile,
174    PreferenceWatcher* preference_watcher,
175    DriveServiceInterface* test_drive_service,
176    const base::FilePath& test_cache_root,
177    FileSystemInterface* test_file_system)
178    : profile_(profile),
179      state_(NOT_INITIALIZED),
180      enabled_(false),
181      mounted_(false),
182      cache_root_directory_(!test_cache_root.empty() ?
183                            test_cache_root : util::GetCacheRootPath(profile)),
184      weak_ptr_factory_(this) {
185  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
186
187  base::SequencedWorkerPool* blocking_pool = BrowserThread::GetBlockingPool();
188  blocking_task_runner_ = blocking_pool->GetSequencedTaskRunner(
189      blocking_pool->GetSequenceToken());
190
191  OAuth2TokenService* oauth_service =
192      ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
193
194  if (test_drive_service) {
195    drive_service_.reset(test_drive_service);
196  } else if (util::IsDriveV2ApiEnabled()) {
197    drive_service_.reset(new DriveAPIService(
198        oauth_service,
199        g_browser_process->system_request_context(),
200        blocking_task_runner_.get(),
201        GURL(google_apis::DriveApiUrlGenerator::kBaseUrlForProduction),
202        GURL(google_apis::DriveApiUrlGenerator::kBaseDownloadUrlForProduction),
203        GURL(google_apis::GDataWapiUrlGenerator::kBaseUrlForProduction),
204        GetDriveUserAgent()));
205  } else {
206    drive_service_.reset(new GDataWapiService(
207        oauth_service,
208        g_browser_process->system_request_context(),
209        blocking_task_runner_.get(),
210        GURL(google_apis::GDataWapiUrlGenerator::kBaseUrlForProduction),
211        GURL(google_apis::GDataWapiUrlGenerator::kBaseDownloadUrlForProduction),
212        GetDriveUserAgent()));
213  }
214  scheduler_.reset(new JobScheduler(
215      profile_->GetPrefs(),
216      drive_service_.get(),
217      blocking_task_runner_.get()));
218  metadata_storage_.reset(new internal::ResourceMetadataStorage(
219      cache_root_directory_.Append(kMetadataDirectory),
220      blocking_task_runner_.get()));
221  cache_.reset(new internal::FileCache(
222      metadata_storage_.get(),
223      cache_root_directory_.Append(kCacheFileDirectory),
224      blocking_task_runner_.get(),
225      NULL /* free_disk_space_getter */));
226  drive_app_registry_.reset(new DriveAppRegistry(scheduler_.get()));
227
228  resource_metadata_.reset(new internal::ResourceMetadata(
229      metadata_storage_.get(), blocking_task_runner_));
230
231  file_system_.reset(
232      test_file_system ? test_file_system : new FileSystem(
233          profile_->GetPrefs(),
234          cache_.get(),
235          drive_service_.get(),
236          scheduler_.get(),
237          resource_metadata_.get(),
238          blocking_task_runner_.get(),
239          cache_root_directory_.Append(kTemporaryFileDirectory)));
240  download_handler_.reset(new DownloadHandler(file_system()));
241  debug_info_collector_.reset(
242      new DebugInfoCollector(file_system(), cache_.get()));
243
244  if (preference_watcher) {
245    preference_watcher_.reset(preference_watcher);
246    preference_watcher->set_integration_service(this);
247  }
248}
249
250DriveIntegrationService::~DriveIntegrationService() {
251  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
252}
253
254void DriveIntegrationService::Initialize() {
255  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
256  DCHECK_EQ(NOT_INITIALIZED, state_);
257
258  state_ = INITIALIZING;
259  drive_service_->Initialize();
260  file_system_->Initialize();
261
262  base::PostTaskAndReplyWithResult(
263      blocking_task_runner_.get(),
264      FROM_HERE,
265      base::Bind(&InitializeMetadata,
266                 cache_root_directory_,
267                 metadata_storage_.get(),
268                 cache_.get(),
269                 resource_metadata_.get()),
270      base::Bind(&DriveIntegrationService::InitializeAfterMetadataInitialized,
271                 weak_ptr_factory_.GetWeakPtr()));
272}
273
274void DriveIntegrationService::Shutdown() {
275  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
276
277  weak_ptr_factory_.InvalidateWeakPtrs();
278
279  DriveNotificationManager* drive_notification_manager =
280      DriveNotificationManagerFactory::GetForBrowserContext(profile_);
281  if (drive_notification_manager)
282    drive_notification_manager->RemoveObserver(this);
283
284  RemoveDriveMountPoint();
285  debug_info_collector_.reset();
286  download_handler_.reset();
287  file_system_.reset();
288  drive_app_registry_.reset();
289  scheduler_.reset();
290  drive_service_.reset();
291}
292
293void DriveIntegrationService::SetEnabled(bool enabled) {
294  enabled_ = enabled;
295  // TODO(hidehiko): Implement actual enable procedure.
296}
297
298void DriveIntegrationService::AddObserver(
299    DriveIntegrationServiceObserver* observer) {
300  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
301  observers_.AddObserver(observer);
302}
303
304void DriveIntegrationService::RemoveObserver(
305    DriveIntegrationServiceObserver* observer) {
306  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
307  observers_.RemoveObserver(observer);
308}
309
310void DriveIntegrationService::OnNotificationReceived() {
311  file_system_->CheckForUpdates();
312  drive_app_registry_->Update();
313}
314
315void DriveIntegrationService::OnPushNotificationEnabled(bool enabled) {
316  if (enabled)
317    drive_app_registry_->Update();
318
319  const char* status = (enabled ? "enabled" : "disabled");
320  util::Log(logging::LOG_INFO, "Push notification is %s", status);
321}
322
323bool DriveIntegrationService::IsDriveEnabled() {
324  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
325
326  if (!util::IsDriveEnabledForProfile(profile_))
327    return false;
328
329  // For backword compatibility, REMOUNTING also means enabled.
330  return state_ == INITIALIZED || state_ == REMOUNTING;
331}
332
333void DriveIntegrationService::ClearCacheAndRemountFileSystem(
334    const base::Callback<void(bool)>& callback) {
335  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
336  DCHECK(!callback.is_null());
337
338  if (state_ != INITIALIZED) {
339    callback.Run(false);
340    return;
341  }
342
343  state_ = REMOUNTING;
344
345  RemoveDriveMountPoint();
346  // Reloading the file system will clear the resource metadata.
347  file_system_->Reload();
348  // Reload the Drive app registry too.
349  drive_app_registry_->Update();
350
351  cache_->ClearAllOnUIThread(base::Bind(
352      &DriveIntegrationService::AddBackDriveMountPoint,
353      weak_ptr_factory_.GetWeakPtr(),
354      callback));
355}
356
357void DriveIntegrationService::AddBackDriveMountPoint(
358    const base::Callback<void(bool)>& callback,
359    bool success) {
360  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
361  DCHECK(!callback.is_null());
362
363  if (!success) {
364    callback.Run(false);
365    return;
366  }
367
368  file_system_->Initialize();
369  drive_app_registry_->Update();
370  AddDriveMountPoint();
371
372  callback.Run(true);
373}
374
375void DriveIntegrationService::AddDriveMountPoint() {
376  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
377
378  const base::FilePath drive_mount_point = util::GetDriveMountPointPath();
379  fileapi::ExternalMountPoints* mount_points =
380      BrowserContext::GetMountPoints(profile_);
381  DCHECK(mount_points);
382
383  bool success = mount_points->RegisterFileSystem(
384      drive_mount_point.BaseName().AsUTF8Unsafe(),
385      fileapi::kFileSystemTypeDrive,
386      drive_mount_point);
387
388  if (success) {
389    state_ = INITIALIZED;
390    mounted_ = true;
391    util::Log(logging::LOG_INFO, "Drive mount point is added");
392    FOR_EACH_OBSERVER(DriveIntegrationServiceObserver, observers_,
393                      OnFileSystemMounted());
394  } else {
395    state_ = NOT_INITIALIZED;
396  }
397}
398
399void DriveIntegrationService::RemoveDriveMountPoint() {
400  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
401
402  job_list()->CancelAllJobs();
403
404  FOR_EACH_OBSERVER(DriveIntegrationServiceObserver, observers_,
405                    OnFileSystemBeingUnmounted());
406
407  fileapi::ExternalMountPoints* mount_points =
408      BrowserContext::GetMountPoints(profile_);
409  DCHECK(mount_points);
410
411  mounted_ = false;
412  mount_points->RevokeFileSystem(
413      util::GetDriveMountPointPath().BaseName().AsUTF8Unsafe());
414  util::Log(logging::LOG_INFO, "Drive mount point is removed");
415}
416
417void DriveIntegrationService::InitializeAfterMetadataInitialized(
418    FileError error) {
419  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
420
421  if (error != FILE_ERROR_OK) {
422    LOG(WARNING) << "Failed to initialize: " << FileErrorToString(error);
423
424    // Change the download directory to the default value if the download
425    // destination is set to under Drive mount point.
426    PrefService* pref_service = profile_->GetPrefs();
427    if (util::IsUnderDriveMountPoint(
428            pref_service->GetFilePath(prefs::kDownloadDefaultDirectory))) {
429      pref_service->SetFilePath(prefs::kDownloadDefaultDirectory,
430                                DownloadPrefs::GetDefaultDownloadDirectory());
431    }
432
433    // Back to NOT_INITIALIZED state. Then, re-running Initialize() should
434    // work if the error is recovarable manually (such as out of disk space).
435    state_ = NOT_INITIALIZED;
436    return;
437  }
438
439  content::DownloadManager* download_manager =
440      g_browser_process->download_status_updater() ?
441      BrowserContext::GetDownloadManager(profile_) : NULL;
442  download_handler_->Initialize(
443      download_manager,
444      cache_root_directory_.Append(kTemporaryFileDirectory));
445
446  // Register for Google Drive invalidation notifications.
447  DriveNotificationManager* drive_notification_manager =
448      DriveNotificationManagerFactory::GetForBrowserContext(profile_);
449  if (drive_notification_manager) {
450    drive_notification_manager->AddObserver(this);
451    const bool registered =
452        drive_notification_manager->push_notification_registered();
453    const char* status = (registered ? "registered" : "not registered");
454    util::Log(logging::LOG_INFO, "Push notification is %s", status);
455
456    if (drive_notification_manager->push_notification_enabled())
457      drive_app_registry_->Update();
458  }
459
460  AddDriveMountPoint();
461  state_ = INITIALIZED;
462}
463
464//===================== DriveIntegrationServiceFactory =======================
465
466// static
467DriveIntegrationService* DriveIntegrationServiceFactory::GetForProfile(
468    Profile* profile) {
469  DriveIntegrationService* service = GetForProfileRegardlessOfStates(profile);
470  if (service && !service->IsDriveEnabled())
471    return NULL;
472
473  return service;
474}
475
476// static
477DriveIntegrationService*
478DriveIntegrationServiceFactory::GetForProfileRegardlessOfStates(
479    Profile* profile) {
480  return static_cast<DriveIntegrationService*>(
481      GetInstance()->GetServiceForBrowserContext(profile, true));
482}
483
484// static
485DriveIntegrationService* DriveIntegrationServiceFactory::FindForProfile(
486    Profile* profile) {
487  DriveIntegrationService* service = FindForProfileRegardlessOfStates(profile);
488  if (service && !service->IsDriveEnabled())
489    return NULL;
490
491  return service;
492}
493
494// static
495DriveIntegrationService*
496DriveIntegrationServiceFactory::FindForProfileRegardlessOfStates(
497    Profile* profile) {
498  return static_cast<DriveIntegrationService*>(
499      GetInstance()->GetServiceForBrowserContext(profile, false));
500}
501
502// static
503DriveIntegrationServiceFactory* DriveIntegrationServiceFactory::GetInstance() {
504  return Singleton<DriveIntegrationServiceFactory>::get();
505}
506
507// static
508void DriveIntegrationServiceFactory::SetFactoryForTest(
509    const FactoryCallback& factory_for_test) {
510  GetInstance()->factory_for_test_ = factory_for_test;
511}
512
513DriveIntegrationServiceFactory::DriveIntegrationServiceFactory()
514    : BrowserContextKeyedServiceFactory(
515        "DriveIntegrationService",
516        BrowserContextDependencyManager::GetInstance()) {
517  DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
518  DependsOn(DriveNotificationManagerFactory::GetInstance());
519  DependsOn(DownloadServiceFactory::GetInstance());
520}
521
522DriveIntegrationServiceFactory::~DriveIntegrationServiceFactory() {
523}
524
525BrowserContextKeyedService*
526DriveIntegrationServiceFactory::BuildServiceInstanceFor(
527    content::BrowserContext* context) const {
528  Profile* profile = Profile::FromBrowserContext(context);
529
530  DriveIntegrationService* service = NULL;
531  if (factory_for_test_.is_null()) {
532    DriveIntegrationService::PreferenceWatcher* preference_watcher = NULL;
533    if (chromeos::IsProfileAssociatedWithGaiaAccount(profile)) {
534      // Drive File System can be enabled.
535      preference_watcher =
536          new DriveIntegrationService::PreferenceWatcher(profile->GetPrefs());
537    }
538
539    service = new DriveIntegrationService(profile, preference_watcher,
540                                          NULL, base::FilePath(), NULL);
541  } else {
542    service = factory_for_test_.Run(profile);
543  }
544
545  service->Initialize();
546  return service;
547}
548
549}  // namespace drive
550