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