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