Home | History | Annotate | Download | only in drive
      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