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