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