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.
5 #include "chrome/browser/chromeos/drive/drive_integration_service.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 "grit/generated_resources.h"
44 #include "ui/base/l10n/l10n_util.h"
45 #include "webkit/browser/fileapi/external_mount_points.h"
46 #include "webkit/common/user_agent/user_agent_util.h"
48 using content::BrowserContext;
49 using content::BrowserThread;
54 // Name of the directory used to store metadata.
55 const base::FilePath::CharType kMetadataDirectory[] = FILE_PATH_LITERAL("meta");
57 // Name of the directory used to store cached files.
58 const base::FilePath::CharType kCacheFileDirectory[] =
59 FILE_PATH_LITERAL("files");
61 // Name of the directory used to store temporary files.
62 const base::FilePath::CharType kTemporaryFileDirectory[] =
63 FILE_PATH_LITERAL("tmp");
65 // Returns a user agent string used for communicating with the Drive backend,
66 // both WAPI and Drive API. The user agent looks like:
68 // chromedrive-<VERSION> chrome-cc/none (<OS_CPU_INFO>)
69 // chromedrive-24.0.1274.0 chrome-cc/none (CrOS x86_64 0.4.0)
71 // TODO(satorux): Move this function to somewhere else: crbug.com/151605
72 std::string GetDriveUserAgent() {
73 const char kDriveClientName[] = "chromedrive";
75 chrome::VersionInfo version_info;
76 const std::string version = (version_info.is_valid() ?
77 version_info.Version() :
78 std::string("unknown"));
80 // This part is <client_name>/<version>.
81 const char kLibraryInfo[] = "chrome-cc/none";
83 const std::string os_cpu_info = webkit_glue::BuildOSCpuInfo();
85 // Add "gzip" to receive compressed data from the server.
86 // (see https://developers.google.com/drive/performance)
87 return base::StringPrintf("%s-%s %s (%s) (gzip)",
94 // Initializes FileCache and ResourceMetadata.
95 // Must be run on the same task runner used by |cache| and |resource_metadata|.
96 FileError InitializeMetadata(
97 const base::FilePath& cache_root_directory,
98 internal::ResourceMetadataStorage* metadata_storage,
99 internal::FileCache* cache,
100 internal::ResourceMetadata* resource_metadata,
101 const ResourceIdCanonicalizer& id_canonicalizer,
102 const base::FilePath& downloads_directory) {
103 if (!file_util::CreateDirectory(cache_root_directory.Append(
104 kMetadataDirectory)) ||
105 !file_util::CreateDirectory(cache_root_directory.Append(
106 kCacheFileDirectory)) ||
107 !file_util::CreateDirectory(cache_root_directory.Append(
108 kTemporaryFileDirectory))) {
109 LOG(WARNING) << "Failed to create directories.";
110 return FILE_ERROR_FAILED;
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(kCacheFileDirectory),
117 file_util::FILE_PERMISSION_USER_MASK |
118 file_util::FILE_PERMISSION_EXECUTE_BY_GROUP |
119 file_util::FILE_PERMISSION_EXECUTE_BY_OTHERS);
121 internal::ResourceMetadataStorage::UpgradeOldDB(
122 metadata_storage->directory_path(), id_canonicalizer);
124 if (!metadata_storage->Initialize()) {
125 LOG(WARNING) << "Failed to initialize the metadata storage.";
126 return FILE_ERROR_FAILED;
129 if (!cache->Initialize()) {
130 LOG(WARNING) << "Failed to initialize the cache.";
131 return FILE_ERROR_FAILED;
134 if (metadata_storage->cache_file_scan_is_needed()) {
135 // Generate unique directory name.
136 const std::string& dest_directory_name = l10n_util::GetStringUTF8(
137 IDS_FILE_BROWSER_RECOVERED_FILES_FROM_GOOGLE_DRIVE_DIRECTORY_NAME);
138 base::FilePath dest_directory = downloads_directory.Append(
139 base::FilePath::FromUTF8Unsafe(dest_directory_name));
140 for (int uniquifier = 1; base::PathExists(dest_directory); ++uniquifier) {
141 dest_directory = downloads_directory.Append(
142 base::FilePath::FromUTF8Unsafe(dest_directory_name))
143 .InsertBeforeExtensionASCII(base::StringPrintf(" (%d)", uniquifier));
146 internal::ResourceMetadataStorage::RecoveredCacheInfoMap
147 recovered_cache_info;
148 metadata_storage->RecoverCacheInfoFromTrashedResourceMap(
149 &recovered_cache_info);
151 LOG(INFO) << "DB could not be opened for some reasons. "
152 << "Recovering cache files to " << dest_directory.value();
153 if (!cache->RecoverFilesFromCacheDirectory(dest_directory,
154 recovered_cache_info)) {
155 LOG(WARNING) << "Failed to recover cache files.";
156 return FILE_ERROR_FAILED;
160 FileError error = resource_metadata->Initialize();
161 LOG_IF(WARNING, error != FILE_ERROR_OK)
162 << "Failed to initialize resource metadata. " << FileErrorToString(error);
168 // Observes drive disable Preference's change.
169 class DriveIntegrationService::PreferenceWatcher {
171 explicit PreferenceWatcher(PrefService* pref_service)
172 : pref_service_(pref_service),
173 integration_service_(NULL),
174 weak_ptr_factory_(this) {
175 DCHECK(pref_service);
176 pref_change_registrar_.Init(pref_service);
177 pref_change_registrar_.Add(
178 prefs::kDisableDrive,
179 base::Bind(&PreferenceWatcher::OnPreferenceChanged,
180 weak_ptr_factory_.GetWeakPtr()));
183 void set_integration_service(DriveIntegrationService* integration_service) {
184 integration_service_ = integration_service;
188 void OnPreferenceChanged() {
189 DCHECK(integration_service_);
190 integration_service_->SetEnabled(
191 !pref_service_->GetBoolean(prefs::kDisableDrive));
194 PrefService* pref_service_;
195 PrefChangeRegistrar pref_change_registrar_;
196 DriveIntegrationService* integration_service_;
198 base::WeakPtrFactory<PreferenceWatcher> weak_ptr_factory_;
199 DISALLOW_COPY_AND_ASSIGN(PreferenceWatcher);
202 DriveIntegrationService::DriveIntegrationService(
204 PreferenceWatcher* preference_watcher,
205 DriveServiceInterface* test_drive_service,
206 const base::FilePath& test_cache_root,
207 FileSystemInterface* test_file_system)
209 state_(NOT_INITIALIZED),
211 cache_root_directory_(!test_cache_root.empty() ?
212 test_cache_root : util::GetCacheRootPath(profile)),
213 weak_ptr_factory_(this) {
214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
216 base::SequencedWorkerPool* blocking_pool = BrowserThread::GetBlockingPool();
217 blocking_task_runner_ = blocking_pool->GetSequencedTaskRunner(
218 blocking_pool->GetSequenceToken());
220 ProfileOAuth2TokenService* oauth_service =
221 ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
223 if (test_drive_service) {
224 drive_service_.reset(test_drive_service);
225 } else if (util::IsDriveV2ApiEnabled()) {
226 drive_service_.reset(new DriveAPIService(
228 g_browser_process->system_request_context(),
229 blocking_task_runner_.get(),
230 GURL(google_apis::DriveApiUrlGenerator::kBaseUrlForProduction),
231 GURL(google_apis::DriveApiUrlGenerator::kBaseDownloadUrlForProduction),
232 GURL(google_apis::GDataWapiUrlGenerator::kBaseUrlForProduction),
233 GetDriveUserAgent()));
235 drive_service_.reset(new GDataWapiService(
237 g_browser_process->system_request_context(),
238 blocking_task_runner_.get(),
239 GURL(google_apis::GDataWapiUrlGenerator::kBaseUrlForProduction),
240 GURL(google_apis::GDataWapiUrlGenerator::kBaseDownloadUrlForProduction),
241 GetDriveUserAgent()));
243 scheduler_.reset(new JobScheduler(
244 profile_->GetPrefs(),
245 drive_service_.get(),
246 blocking_task_runner_.get()));
247 metadata_storage_.reset(new internal::ResourceMetadataStorage(
248 cache_root_directory_.Append(kMetadataDirectory),
249 blocking_task_runner_.get()));
250 cache_.reset(new internal::FileCache(
251 metadata_storage_.get(),
252 cache_root_directory_.Append(kCacheFileDirectory),
253 blocking_task_runner_.get(),
254 NULL /* free_disk_space_getter */));
255 drive_app_registry_.reset(new DriveAppRegistry(scheduler_.get()));
257 resource_metadata_.reset(new internal::ResourceMetadata(
258 metadata_storage_.get(), blocking_task_runner_));
261 test_file_system ? test_file_system : new FileSystem(
262 profile_->GetPrefs(),
264 drive_service_.get(),
266 resource_metadata_.get(),
267 blocking_task_runner_.get(),
268 cache_root_directory_.Append(kTemporaryFileDirectory)));
269 download_handler_.reset(new DownloadHandler(file_system()));
270 debug_info_collector_.reset(
271 new DebugInfoCollector(file_system(), cache_.get(),
272 blocking_task_runner_.get()));
274 if (preference_watcher) {
275 preference_watcher_.reset(preference_watcher);
276 preference_watcher->set_integration_service(this);
280 DriveIntegrationService::~DriveIntegrationService() {
281 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
284 void DriveIntegrationService::Shutdown() {
285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
287 weak_ptr_factory_.InvalidateWeakPtrs();
289 DriveNotificationManager* drive_notification_manager =
290 DriveNotificationManagerFactory::GetForBrowserContext(profile_);
291 if (drive_notification_manager)
292 drive_notification_manager->RemoveObserver(this);
294 RemoveDriveMountPoint();
295 debug_info_collector_.reset();
296 download_handler_.reset();
297 file_system_.reset();
298 drive_app_registry_.reset();
300 drive_service_.reset();
303 void DriveIntegrationService::SetEnabled(bool enabled) {
304 // Do nothing if not changed.
305 if (enabled_ == enabled)
311 case NOT_INITIALIZED:
312 // If the initialization is not yet done, trigger it.
318 // If the state is INITIALIZING or REMOUNTING, at the end of the
319 // process, it tries to mounting (with re-checking enabled state).
320 // Do nothing for now.
324 // The integration service is already initialized. Add the mount point.
325 AddDriveMountPoint();
330 RemoveDriveMountPoint();
335 bool DriveIntegrationService::IsMounted() const {
336 // Look up the registered path, and just discard it.
337 // GetRegisteredPath() returns true if the path is available.
338 const base::FilePath& drive_mount_point = util::GetDriveMountPointPath();
339 base::FilePath unused;
340 return BrowserContext::GetMountPoints(profile_)->GetRegisteredPath(
341 drive_mount_point.BaseName().AsUTF8Unsafe(), &unused);
344 void DriveIntegrationService::AddObserver(
345 DriveIntegrationServiceObserver* observer) {
346 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
347 observers_.AddObserver(observer);
350 void DriveIntegrationService::RemoveObserver(
351 DriveIntegrationServiceObserver* observer) {
352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
353 observers_.RemoveObserver(observer);
356 void DriveIntegrationService::OnNotificationReceived() {
357 file_system_->CheckForUpdates();
358 drive_app_registry_->Update();
361 void DriveIntegrationService::OnPushNotificationEnabled(bool enabled) {
363 drive_app_registry_->Update();
365 const char* status = (enabled ? "enabled" : "disabled");
366 util::Log(logging::LOG_INFO, "Push notification is %s", status);
369 void DriveIntegrationService::ClearCacheAndRemountFileSystem(
370 const base::Callback<void(bool)>& callback) {
371 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
372 DCHECK(!callback.is_null());
374 if (state_ != INITIALIZED) {
379 RemoveDriveMountPoint();
382 // Reloads the Drive app registry.
383 drive_app_registry_->Update();
384 // Reloading the file system clears resource metadata and cache.
385 file_system_->Reload(base::Bind(
386 &DriveIntegrationService::AddBackDriveMountPoint,
387 weak_ptr_factory_.GetWeakPtr(),
391 void DriveIntegrationService::AddBackDriveMountPoint(
392 const base::Callback<void(bool)>& callback,
394 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
395 DCHECK(!callback.is_null());
397 state_ = error == FILE_ERROR_OK ? INITIALIZED : NOT_INITIALIZED;
399 if (error != FILE_ERROR_OK || !enabled_) {
400 // Failed to reload, or Drive was disabled during the reloading.
405 AddDriveMountPoint();
409 void DriveIntegrationService::AddDriveMountPoint() {
410 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
411 DCHECK_EQ(INITIALIZED, state_);
414 const base::FilePath drive_mount_point = util::GetDriveMountPointPath();
415 fileapi::ExternalMountPoints* mount_points =
416 BrowserContext::GetMountPoints(profile_);
417 DCHECK(mount_points);
419 bool success = mount_points->RegisterFileSystem(
420 drive_mount_point.BaseName().AsUTF8Unsafe(),
421 fileapi::kFileSystemTypeDrive,
425 util::Log(logging::LOG_INFO, "Drive mount point is added");
426 FOR_EACH_OBSERVER(DriveIntegrationServiceObserver, observers_,
427 OnFileSystemMounted());
431 void DriveIntegrationService::RemoveDriveMountPoint() {
432 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
434 job_list()->CancelAllJobs();
436 FOR_EACH_OBSERVER(DriveIntegrationServiceObserver, observers_,
437 OnFileSystemBeingUnmounted());
439 fileapi::ExternalMountPoints* mount_points =
440 BrowserContext::GetMountPoints(profile_);
441 DCHECK(mount_points);
443 mount_points->RevokeFileSystem(
444 util::GetDriveMountPointPath().BaseName().AsUTF8Unsafe());
445 util::Log(logging::LOG_INFO, "Drive mount point is removed");
448 void DriveIntegrationService::Initialize() {
449 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
450 DCHECK_EQ(NOT_INITIALIZED, state_);
453 state_ = INITIALIZING;
455 base::PostTaskAndReplyWithResult(
456 blocking_task_runner_.get(),
458 base::Bind(&InitializeMetadata,
459 cache_root_directory_,
460 metadata_storage_.get(),
462 resource_metadata_.get(),
463 drive_service_->GetResourceIdCanonicalizer(),
464 DownloadPrefs::GetDefaultDownloadDirectory()),
465 base::Bind(&DriveIntegrationService::InitializeAfterMetadataInitialized,
466 weak_ptr_factory_.GetWeakPtr()));
469 void DriveIntegrationService::InitializeAfterMetadataInitialized(
471 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
472 DCHECK_EQ(INITIALIZING, state_);
474 drive_service_->Initialize(
475 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->
476 GetPrimaryAccountId());
478 if (error != FILE_ERROR_OK) {
479 LOG(WARNING) << "Failed to initialize: " << FileErrorToString(error);
481 // Change the download directory to the default value if the download
482 // destination is set to under Drive mount point.
483 PrefService* pref_service = profile_->GetPrefs();
484 if (util::IsUnderDriveMountPoint(
485 pref_service->GetFilePath(prefs::kDownloadDefaultDirectory))) {
486 pref_service->SetFilePath(prefs::kDownloadDefaultDirectory,
487 DownloadPrefs::GetDefaultDownloadDirectory());
490 // Back to NOT_INITIALIZED state. Then, re-running Initialize() should
491 // work if the error is recoverable manually (such as out of disk space).
492 state_ = NOT_INITIALIZED;
496 content::DownloadManager* download_manager =
497 g_browser_process->download_status_updater() ?
498 BrowserContext::GetDownloadManager(profile_) : NULL;
499 download_handler_->Initialize(
501 cache_root_directory_.Append(kTemporaryFileDirectory));
503 // Register for Google Drive invalidation notifications.
504 DriveNotificationManager* drive_notification_manager =
505 DriveNotificationManagerFactory::GetForBrowserContext(profile_);
506 if (drive_notification_manager) {
507 drive_notification_manager->AddObserver(this);
508 const bool registered =
509 drive_notification_manager->push_notification_registered();
510 const char* status = (registered ? "registered" : "not registered");
511 util::Log(logging::LOG_INFO, "Push notification is %s", status);
513 if (drive_notification_manager->push_notification_enabled())
514 drive_app_registry_->Update();
517 state_ = INITIALIZED;
519 // Mount only when the drive is enabled. Initialize is triggered by
520 // SetEnabled(true), but there is a change to disable it again during
521 // the metadata initialization, so we need to look this up again here.
523 AddDriveMountPoint();
526 //===================== DriveIntegrationServiceFactory =======================
529 DriveIntegrationService* DriveIntegrationServiceFactory::GetForProfile(
531 return GetForProfileRegardlessOfStates(profile);
535 DriveIntegrationService*
536 DriveIntegrationServiceFactory::GetForProfileRegardlessOfStates(
538 return static_cast<DriveIntegrationService*>(
539 GetInstance()->GetServiceForBrowserContext(profile, true));
543 DriveIntegrationService* DriveIntegrationServiceFactory::FindForProfile(
545 return FindForProfileRegardlessOfStates(profile);
549 DriveIntegrationService*
550 DriveIntegrationServiceFactory::FindForProfileRegardlessOfStates(
552 return static_cast<DriveIntegrationService*>(
553 GetInstance()->GetServiceForBrowserContext(profile, false));
557 DriveIntegrationServiceFactory* DriveIntegrationServiceFactory::GetInstance() {
558 return Singleton<DriveIntegrationServiceFactory>::get();
562 void DriveIntegrationServiceFactory::SetFactoryForTest(
563 const FactoryCallback& factory_for_test) {
564 GetInstance()->factory_for_test_ = factory_for_test;
567 DriveIntegrationServiceFactory::DriveIntegrationServiceFactory()
568 : BrowserContextKeyedServiceFactory(
569 "DriveIntegrationService",
570 BrowserContextDependencyManager::GetInstance()) {
571 DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
572 DependsOn(DriveNotificationManagerFactory::GetInstance());
573 DependsOn(DownloadServiceFactory::GetInstance());
576 DriveIntegrationServiceFactory::~DriveIntegrationServiceFactory() {
579 BrowserContextKeyedService*
580 DriveIntegrationServiceFactory::BuildServiceInstanceFor(
581 content::BrowserContext* context) const {
582 Profile* profile = Profile::FromBrowserContext(context);
584 DriveIntegrationService* service = NULL;
585 if (factory_for_test_.is_null()) {
586 DriveIntegrationService::PreferenceWatcher* preference_watcher = NULL;
587 if (chromeos::IsProfileAssociatedWithGaiaAccount(profile)) {
588 // Drive File System can be enabled.
590 new DriveIntegrationService::PreferenceWatcher(profile->GetPrefs());
593 service = new DriveIntegrationService(profile, preference_watcher,
594 NULL, base::FilePath(), NULL);
596 service = factory_for_test_.Run(profile);
599 service->SetEnabled(drive::util::IsDriveEnabledForProfile(profile));