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