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/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/job_scheduler.h"
20 #include "chrome/browser/chromeos/drive/resource_metadata.h"
21 #include "chrome/browser/chromeos/drive/resource_metadata_storage.h"
22 #include "chrome/browser/chromeos/file_manager/path_util.h"
23 #include "chrome/browser/chromeos/profiles/profile_util.h"
24 #include "chrome/browser/download/download_prefs.h"
25 #include "chrome/browser/download/download_service.h"
26 #include "chrome/browser/download/download_service_factory.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_app_registry.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/event_logger.h"
33 #include "chrome/browser/drive/gdata_wapi_service.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/browser/signin/signin_manager.h"
38 #include "chrome/browser/signin/signin_manager_factory.h"
39 #include "chrome/common/chrome_version_info.h"
40 #include "chrome/common/pref_names.h"
41 #include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
42 #include "content/public/browser/browser_context.h"
43 #include "content/public/browser/browser_thread.h"
44 #include "google_apis/drive/auth_service.h"
45 #include "google_apis/drive/gdata_wapi_url_generator.h"
46 #include "grit/generated_resources.h"
47 #include "ui/base/l10n/l10n_util.h"
48 #include "webkit/browser/fileapi/external_mount_points.h"
49 #include "webkit/common/user_agent/user_agent_util.h"
51 using content::BrowserContext;
52 using content::BrowserThread;
57 // Name of the directory used to store metadata.
58 const base::FilePath::CharType kMetadataDirectory[] = FILE_PATH_LITERAL("meta");
60 // Name of the directory used to store cached files.
61 const base::FilePath::CharType kCacheFileDirectory[] =
62 FILE_PATH_LITERAL("files");
64 // Name of the directory used to store temporary files.
65 const base::FilePath::CharType kTemporaryFileDirectory[] =
66 FILE_PATH_LITERAL("tmp");
68 // Returns a user agent string used for communicating with the Drive backend,
69 // both WAPI and Drive API. The user agent looks like:
71 // chromedrive-<VERSION> chrome-cc/none (<OS_CPU_INFO>)
72 // chromedrive-24.0.1274.0 chrome-cc/none (CrOS x86_64 0.4.0)
74 // TODO(satorux): Move this function to somewhere else: crbug.com/151605
75 std::string GetDriveUserAgent() {
76 const char kDriveClientName[] = "chromedrive";
78 chrome::VersionInfo version_info;
79 const std::string version = (version_info.is_valid() ?
80 version_info.Version() :
81 std::string("unknown"));
83 // This part is <client_name>/<version>.
84 const char kLibraryInfo[] = "chrome-cc/none";
86 const std::string os_cpu_info = webkit_glue::BuildOSCpuInfo();
88 // Add "gzip" to receive compressed data from the server.
89 // (see https://developers.google.com/drive/performance)
90 return base::StringPrintf("%s-%s %s (%s) (gzip)",
97 // Initializes FileCache and ResourceMetadata.
98 // Must be run on the same task runner used by |cache| and |resource_metadata|.
99 FileError InitializeMetadata(
100 const base::FilePath& cache_root_directory,
101 internal::ResourceMetadataStorage* metadata_storage,
102 internal::FileCache* cache,
103 internal::ResourceMetadata* resource_metadata,
104 const ResourceIdCanonicalizer& id_canonicalizer,
105 const base::FilePath& downloads_directory) {
106 if (!base::CreateDirectory(cache_root_directory.Append(
107 kMetadataDirectory)) ||
108 !base::CreateDirectory(cache_root_directory.Append(
109 kCacheFileDirectory)) ||
110 !base::CreateDirectory(cache_root_directory.Append(
111 kTemporaryFileDirectory))) {
112 LOG(WARNING) << "Failed to create directories.";
113 return FILE_ERROR_FAILED;
116 // Change permissions of cache file directory to u+rwx,og+x (711) in order to
117 // allow archive files in that directory to be mounted by cros-disks.
118 base::SetPosixFilePermissions(
119 cache_root_directory.Append(kCacheFileDirectory),
120 base::FILE_PERMISSION_USER_MASK |
121 base::FILE_PERMISSION_EXECUTE_BY_GROUP |
122 base::FILE_PERMISSION_EXECUTE_BY_OTHERS);
124 internal::ResourceMetadataStorage::UpgradeOldDB(
125 metadata_storage->directory_path(), id_canonicalizer);
127 if (!metadata_storage->Initialize()) {
128 LOG(WARNING) << "Failed to initialize the metadata storage.";
129 return FILE_ERROR_FAILED;
132 if (!cache->Initialize()) {
133 LOG(WARNING) << "Failed to initialize the cache.";
134 return FILE_ERROR_FAILED;
137 if (metadata_storage->cache_file_scan_is_needed()) {
138 // Generate unique directory name.
139 const std::string& dest_directory_name = l10n_util::GetStringUTF8(
140 IDS_FILE_BROWSER_RECOVERED_FILES_FROM_GOOGLE_DRIVE_DIRECTORY_NAME);
141 base::FilePath dest_directory = downloads_directory.Append(
142 base::FilePath::FromUTF8Unsafe(dest_directory_name));
143 for (int uniquifier = 1; base::PathExists(dest_directory); ++uniquifier) {
144 dest_directory = downloads_directory.Append(
145 base::FilePath::FromUTF8Unsafe(dest_directory_name))
146 .InsertBeforeExtensionASCII(base::StringPrintf(" (%d)", uniquifier));
149 internal::ResourceMetadataStorage::RecoveredCacheInfoMap
150 recovered_cache_info;
151 metadata_storage->RecoverCacheInfoFromTrashedResourceMap(
152 &recovered_cache_info);
154 LOG_IF(WARNING, !recovered_cache_info.empty())
155 << "DB could not be opened for some reasons. "
156 << "Recovering cache files to " << dest_directory.value();
157 if (!cache->RecoverFilesFromCacheDirectory(dest_directory,
158 recovered_cache_info)) {
159 LOG(WARNING) << "Failed to recover cache files.";
160 return FILE_ERROR_FAILED;
164 FileError error = resource_metadata->Initialize();
165 LOG_IF(WARNING, error != FILE_ERROR_OK)
166 << "Failed to initialize resource metadata. " << FileErrorToString(error);
172 // Observes drive disable Preference's change.
173 class DriveIntegrationService::PreferenceWatcher {
175 explicit PreferenceWatcher(PrefService* pref_service)
176 : pref_service_(pref_service),
177 integration_service_(NULL),
178 weak_ptr_factory_(this) {
179 DCHECK(pref_service);
180 pref_change_registrar_.Init(pref_service);
181 pref_change_registrar_.Add(
182 prefs::kDisableDrive,
183 base::Bind(&PreferenceWatcher::OnPreferenceChanged,
184 weak_ptr_factory_.GetWeakPtr()));
187 void set_integration_service(DriveIntegrationService* integration_service) {
188 integration_service_ = integration_service;
192 void OnPreferenceChanged() {
193 DCHECK(integration_service_);
194 integration_service_->SetEnabled(
195 !pref_service_->GetBoolean(prefs::kDisableDrive));
198 PrefService* pref_service_;
199 PrefChangeRegistrar pref_change_registrar_;
200 DriveIntegrationService* integration_service_;
202 base::WeakPtrFactory<PreferenceWatcher> weak_ptr_factory_;
203 DISALLOW_COPY_AND_ASSIGN(PreferenceWatcher);
206 DriveIntegrationService::DriveIntegrationService(
208 PreferenceWatcher* preference_watcher,
209 DriveServiceInterface* test_drive_service,
210 const std::string& test_mount_point_name,
211 const base::FilePath& test_cache_root,
212 FileSystemInterface* test_file_system)
214 state_(NOT_INITIALIZED),
216 mount_point_name_(test_mount_point_name),
217 cache_root_directory_(!test_cache_root.empty() ?
218 test_cache_root : util::GetCacheRootPath(profile)),
219 weak_ptr_factory_(this) {
220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
222 logger_.reset(new EventLogger);
223 base::SequencedWorkerPool* blocking_pool = BrowserThread::GetBlockingPool();
224 blocking_task_runner_ = blocking_pool->GetSequencedTaskRunner(
225 blocking_pool->GetSequenceToken());
227 ProfileOAuth2TokenService* oauth_service =
228 ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
230 if (test_drive_service) {
231 drive_service_.reset(test_drive_service);
232 } else if (util::IsDriveV2ApiEnabled()) {
233 drive_service_.reset(new DriveAPIService(
235 g_browser_process->system_request_context(),
236 blocking_task_runner_.get(),
237 GURL(google_apis::DriveApiUrlGenerator::kBaseUrlForProduction),
238 GURL(google_apis::DriveApiUrlGenerator::kBaseDownloadUrlForProduction),
239 GURL(google_apis::GDataWapiUrlGenerator::kBaseUrlForProduction),
240 GetDriveUserAgent()));
242 drive_service_.reset(new GDataWapiService(
244 g_browser_process->system_request_context(),
245 blocking_task_runner_.get(),
246 GURL(google_apis::GDataWapiUrlGenerator::kBaseUrlForProduction),
247 GURL(google_apis::GDataWapiUrlGenerator::kBaseDownloadUrlForProduction),
248 GetDriveUserAgent()));
250 scheduler_.reset(new JobScheduler(
251 profile_->GetPrefs(),
253 drive_service_.get(),
254 blocking_task_runner_.get()));
255 metadata_storage_.reset(new internal::ResourceMetadataStorage(
256 cache_root_directory_.Append(kMetadataDirectory),
257 blocking_task_runner_.get()));
258 cache_.reset(new internal::FileCache(
259 metadata_storage_.get(),
260 cache_root_directory_.Append(kCacheFileDirectory),
261 blocking_task_runner_.get(),
262 NULL /* free_disk_space_getter */));
263 drive_app_registry_.reset(new DriveAppRegistry(drive_service_.get()));
265 resource_metadata_.reset(new internal::ResourceMetadata(
266 metadata_storage_.get(), blocking_task_runner_));
269 test_file_system ? test_file_system : new FileSystem(
270 profile_->GetPrefs(),
273 drive_service_.get(),
275 resource_metadata_.get(),
276 blocking_task_runner_.get(),
277 cache_root_directory_.Append(kTemporaryFileDirectory)));
278 download_handler_.reset(new DownloadHandler(file_system()));
279 debug_info_collector_.reset(new DebugInfoCollector(
280 cache_.get(), resource_metadata_.get(), file_system(),
281 blocking_task_runner_.get()));
283 if (preference_watcher) {
284 preference_watcher_.reset(preference_watcher);
285 preference_watcher->set_integration_service(this);
288 SetEnabled(drive::util::IsDriveEnabledForProfile(profile));
291 DriveIntegrationService::~DriveIntegrationService() {
292 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
295 void DriveIntegrationService::Shutdown() {
296 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
298 weak_ptr_factory_.InvalidateWeakPtrs();
300 DriveNotificationManager* drive_notification_manager =
301 DriveNotificationManagerFactory::GetForBrowserContext(profile_);
302 if (drive_notification_manager)
303 drive_notification_manager->RemoveObserver(this);
305 RemoveDriveMountPoint();
306 debug_info_collector_.reset();
307 download_handler_.reset();
308 file_system_.reset();
309 drive_app_registry_.reset();
311 drive_service_.reset();
314 void DriveIntegrationService::SetEnabled(bool enabled) {
315 // If Drive is being disabled, ensure the download destination preference to
316 // be out of Drive. Do this before "Do nothing if not changed." because we
317 // want to run the check for the first SetEnabled() called in the constructor,
318 // which may be a change from false to false.
320 AvoidDriveAsDownloadDirecotryPreference();
322 // Do nothing if not changed.
323 if (enabled_ == enabled)
329 case NOT_INITIALIZED:
330 // If the initialization is not yet done, trigger it.
336 // If the state is INITIALIZING or REMOUNTING, at the end of the
337 // process, it tries to mounting (with re-checking enabled state).
338 // Do nothing for now.
342 // The integration service is already initialized. Add the mount point.
343 AddDriveMountPoint();
348 RemoveDriveMountPoint();
353 bool DriveIntegrationService::IsMounted() const {
354 if (mount_point_name_.empty())
357 // Look up the registered path, and just discard it.
358 // GetRegisteredPath() returns true if the path is available.
359 base::FilePath unused;
360 fileapi::ExternalMountPoints* const mount_points =
361 fileapi::ExternalMountPoints::GetSystemInstance();
362 DCHECK(mount_points);
363 return mount_points->GetRegisteredPath(mount_point_name_, &unused);
366 void DriveIntegrationService::AddObserver(
367 DriveIntegrationServiceObserver* observer) {
368 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
369 observers_.AddObserver(observer);
372 void DriveIntegrationService::RemoveObserver(
373 DriveIntegrationServiceObserver* observer) {
374 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
375 observers_.RemoveObserver(observer);
378 void DriveIntegrationService::OnNotificationReceived() {
379 file_system_->CheckForUpdates();
380 drive_app_registry_->Update();
383 void DriveIntegrationService::OnPushNotificationEnabled(bool enabled) {
385 drive_app_registry_->Update();
387 const char* status = (enabled ? "enabled" : "disabled");
388 logger_->Log(logging::LOG_INFO, "Push notification is %s", status);
391 void DriveIntegrationService::ClearCacheAndRemountFileSystem(
392 const base::Callback<void(bool)>& callback) {
393 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
394 DCHECK(!callback.is_null());
396 if (state_ != INITIALIZED) {
401 RemoveDriveMountPoint();
404 // Reloads the Drive app registry.
405 drive_app_registry_->Update();
406 // Resetting the file system clears resource metadata and cache.
407 file_system_->Reset(base::Bind(
408 &DriveIntegrationService::AddBackDriveMountPoint,
409 weak_ptr_factory_.GetWeakPtr(),
413 void DriveIntegrationService::AddBackDriveMountPoint(
414 const base::Callback<void(bool)>& callback,
416 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
417 DCHECK(!callback.is_null());
419 state_ = error == FILE_ERROR_OK ? INITIALIZED : NOT_INITIALIZED;
421 if (error != FILE_ERROR_OK || !enabled_) {
422 // Failed to reset, or Drive was disabled during the reset.
427 AddDriveMountPoint();
431 void DriveIntegrationService::AddDriveMountPoint() {
432 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
433 DCHECK_EQ(INITIALIZED, state_);
436 const base::FilePath& drive_mount_point =
437 util::GetDriveMountPointPath(profile_);
438 if (mount_point_name_.empty())
439 mount_point_name_ = drive_mount_point.BaseName().AsUTF8Unsafe();
440 fileapi::ExternalMountPoints* const mount_points =
441 fileapi::ExternalMountPoints::GetSystemInstance();
442 DCHECK(mount_points);
444 bool success = mount_points->RegisterFileSystem(
446 fileapi::kFileSystemTypeDrive,
447 fileapi::FileSystemMountOption(),
451 logger_->Log(logging::LOG_INFO, "Drive mount point is added");
452 FOR_EACH_OBSERVER(DriveIntegrationServiceObserver, observers_,
453 OnFileSystemMounted());
457 void DriveIntegrationService::RemoveDriveMountPoint() {
458 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
460 if (!mount_point_name_.empty()) {
461 job_list()->CancelAllJobs();
463 FOR_EACH_OBSERVER(DriveIntegrationServiceObserver, observers_,
464 OnFileSystemBeingUnmounted());
466 fileapi::ExternalMountPoints* const mount_points =
467 fileapi::ExternalMountPoints::GetSystemInstance();
468 DCHECK(mount_points);
470 mount_points->RevokeFileSystem(mount_point_name_);
471 logger_->Log(logging::LOG_INFO, "Drive mount point is removed");
475 void DriveIntegrationService::Initialize() {
476 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
477 DCHECK_EQ(NOT_INITIALIZED, state_);
480 state_ = INITIALIZING;
482 base::PostTaskAndReplyWithResult(
483 blocking_task_runner_.get(),
485 base::Bind(&InitializeMetadata,
486 cache_root_directory_,
487 metadata_storage_.get(),
489 resource_metadata_.get(),
490 drive_service_->GetResourceIdCanonicalizer(),
491 file_manager::util::GetDownloadsFolderForProfile(profile_)),
492 base::Bind(&DriveIntegrationService::InitializeAfterMetadataInitialized,
493 weak_ptr_factory_.GetWeakPtr()));
496 void DriveIntegrationService::InitializeAfterMetadataInitialized(
498 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
499 DCHECK_EQ(INITIALIZING, state_);
501 SigninManagerBase* signin_manager =
502 SigninManagerFactory::GetForProfile(profile_);
503 drive_service_->Initialize(signin_manager->GetAuthenticatedAccountId());
505 if (error != FILE_ERROR_OK) {
506 LOG(WARNING) << "Failed to initialize: " << FileErrorToString(error);
508 // Cannot used Drive. Set the download destination preference out of Drive.
509 AvoidDriveAsDownloadDirecotryPreference();
511 // Back to NOT_INITIALIZED state. Then, re-running Initialize() should
512 // work if the error is recoverable manually (such as out of disk space).
513 state_ = NOT_INITIALIZED;
517 content::DownloadManager* download_manager =
518 g_browser_process->download_status_updater() ?
519 BrowserContext::GetDownloadManager(profile_) : NULL;
520 download_handler_->Initialize(
522 cache_root_directory_.Append(kTemporaryFileDirectory));
524 // Register for Google Drive invalidation notifications.
525 DriveNotificationManager* drive_notification_manager =
526 DriveNotificationManagerFactory::GetForBrowserContext(profile_);
527 if (drive_notification_manager) {
528 drive_notification_manager->AddObserver(this);
529 const bool registered =
530 drive_notification_manager->push_notification_registered();
531 const char* status = (registered ? "registered" : "not registered");
532 logger_->Log(logging::LOG_INFO, "Push notification is %s", status);
534 if (drive_notification_manager->push_notification_enabled())
535 drive_app_registry_->Update();
538 state_ = INITIALIZED;
540 // Mount only when the drive is enabled. Initialize is triggered by
541 // SetEnabled(true), but there is a change to disable it again during
542 // the metadata initialization, so we need to look this up again here.
544 AddDriveMountPoint();
547 void DriveIntegrationService::AvoidDriveAsDownloadDirecotryPreference() {
548 PrefService* pref_service = profile_->GetPrefs();
549 if (util::IsUnderDriveMountPoint(
550 pref_service->GetFilePath(prefs::kDownloadDefaultDirectory))) {
551 pref_service->SetFilePath(
552 prefs::kDownloadDefaultDirectory,
553 file_manager::util::GetDownloadsFolderForProfile(profile_));
557 //===================== DriveIntegrationServiceFactory =======================
559 DriveIntegrationServiceFactory::FactoryCallback*
560 DriveIntegrationServiceFactory::factory_for_test_ = NULL;
562 DriveIntegrationServiceFactory::ScopedFactoryForTest::ScopedFactoryForTest(
563 FactoryCallback* factory_for_test) {
564 factory_for_test_ = factory_for_test;
567 DriveIntegrationServiceFactory::ScopedFactoryForTest::~ScopedFactoryForTest() {
568 factory_for_test_ = NULL;
572 DriveIntegrationService* DriveIntegrationServiceFactory::GetForProfile(
574 return GetForProfileRegardlessOfStates(profile);
578 DriveIntegrationService*
579 DriveIntegrationServiceFactory::GetForProfileRegardlessOfStates(
581 return static_cast<DriveIntegrationService*>(
582 GetInstance()->GetServiceForBrowserContext(profile, true));
586 DriveIntegrationService* DriveIntegrationServiceFactory::FindForProfile(
588 return FindForProfileRegardlessOfStates(profile);
592 DriveIntegrationService*
593 DriveIntegrationServiceFactory::FindForProfileRegardlessOfStates(
595 return static_cast<DriveIntegrationService*>(
596 GetInstance()->GetServiceForBrowserContext(profile, false));
600 DriveIntegrationServiceFactory* DriveIntegrationServiceFactory::GetInstance() {
601 return Singleton<DriveIntegrationServiceFactory>::get();
604 DriveIntegrationServiceFactory::DriveIntegrationServiceFactory()
605 : BrowserContextKeyedServiceFactory(
606 "DriveIntegrationService",
607 BrowserContextDependencyManager::GetInstance()) {
608 DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
609 DependsOn(DriveNotificationManagerFactory::GetInstance());
610 DependsOn(DownloadServiceFactory::GetInstance());
613 DriveIntegrationServiceFactory::~DriveIntegrationServiceFactory() {
616 BrowserContextKeyedService*
617 DriveIntegrationServiceFactory::BuildServiceInstanceFor(
618 content::BrowserContext* context) const {
619 Profile* profile = Profile::FromBrowserContext(context);
621 DriveIntegrationService* service = NULL;
622 if (!factory_for_test_) {
623 DriveIntegrationService::PreferenceWatcher* preference_watcher = NULL;
624 if (chromeos::IsProfileAssociatedWithGaiaAccount(profile)) {
625 // Drive File System can be enabled.
627 new DriveIntegrationService::PreferenceWatcher(profile->GetPrefs());
630 service = new DriveIntegrationService(
631 profile, preference_watcher,
632 NULL, std::string(), base::FilePath(), NULL);
634 service = factory_for_test_->Run(profile);