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/file_manager/path_util.h"
25#include "chrome/browser/chromeos/profiles/profile_util.h"
26#include "chrome/browser/download/download_prefs.h"
27#include "chrome/browser/download/download_service.h"
28#include "chrome/browser/download/download_service_factory.h"
29#include "chrome/browser/drive/drive_api_service.h"
30#include "chrome/browser/drive/drive_api_util.h"
31#include "chrome/browser/drive/drive_notification_manager.h"
32#include "chrome/browser/drive/drive_notification_manager_factory.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/common/chrome_version_info.h"
38#include "chrome/common/pref_names.h"
39#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
40#include "content/public/browser/browser_context.h"
41#include "content/public/browser/browser_thread.h"
42#include "google_apis/drive/auth_service.h"
43#include "google_apis/drive/gdata_wapi_url_generator.h"
44#include "grit/generated_resources.h"
45#include "ui/base/l10n/l10n_util.h"
46#include "webkit/browser/fileapi/external_mount_points.h"
47#include "webkit/common/user_agent/user_agent_util.h"
48
49using content::BrowserContext;
50using content::BrowserThread;
51
52namespace drive {
53namespace {
54
55// Name of the directory used to store metadata.
56const base::FilePath::CharType kMetadataDirectory[] = FILE_PATH_LITERAL("meta");
57
58// Name of the directory used to store cached files.
59const base::FilePath::CharType kCacheFileDirectory[] =
60    FILE_PATH_LITERAL("files");
61
62// Name of the directory used to store temporary files.
63const base::FilePath::CharType kTemporaryFileDirectory[] =
64    FILE_PATH_LITERAL("tmp");
65
66// Returns a user agent string used for communicating with the Drive backend,
67// both WAPI and Drive API.  The user agent looks like:
68//
69// chromedrive-<VERSION> chrome-cc/none (<OS_CPU_INFO>)
70// chromedrive-24.0.1274.0 chrome-cc/none (CrOS x86_64 0.4.0)
71//
72// TODO(satorux): Move this function to somewhere else: crbug.com/151605
73std::string GetDriveUserAgent() {
74  const char kDriveClientName[] = "chromedrive";
75
76  chrome::VersionInfo version_info;
77  const std::string version = (version_info.is_valid() ?
78                               version_info.Version() :
79                               std::string("unknown"));
80
81  // This part is <client_name>/<version>.
82  const char kLibraryInfo[] = "chrome-cc/none";
83
84  const std::string os_cpu_info = webkit_glue::BuildOSCpuInfo();
85
86  // Add "gzip" to receive compressed data from the server.
87  // (see https://developers.google.com/drive/performance)
88  return base::StringPrintf("%s-%s %s (%s) (gzip)",
89                            kDriveClientName,
90                            version.c_str(),
91                            kLibraryInfo,
92                            os_cpu_info.c_str());
93}
94
95// Initializes FileCache and ResourceMetadata.
96// Must be run on the same task runner used by |cache| and |resource_metadata|.
97FileError InitializeMetadata(
98    const base::FilePath& cache_root_directory,
99    internal::ResourceMetadataStorage* metadata_storage,
100    internal::FileCache* cache,
101    internal::ResourceMetadata* resource_metadata,
102    const ResourceIdCanonicalizer& id_canonicalizer,
103    const base::FilePath& downloads_directory) {
104  if (!base::CreateDirectory(cache_root_directory.Append(
105          kMetadataDirectory)) ||
106      !base::CreateDirectory(cache_root_directory.Append(
107          kCacheFileDirectory)) ||
108      !base::CreateDirectory(cache_root_directory.Append(
109          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  base::SetPosixFilePermissions(
117      cache_root_directory.Append(kCacheFileDirectory),
118      base::FILE_PERMISSION_USER_MASK |
119      base::FILE_PERMISSION_EXECUTE_BY_GROUP |
120      base::FILE_PERMISSION_EXECUTE_BY_OTHERS);
121
122  internal::ResourceMetadataStorage::UpgradeOldDB(
123      metadata_storage->directory_path(), id_canonicalizer);
124
125  if (!metadata_storage->Initialize()) {
126    LOG(WARNING) << "Failed to initialize the metadata storage.";
127    return FILE_ERROR_FAILED;
128  }
129
130  if (!cache->Initialize()) {
131    LOG(WARNING) << "Failed to initialize the cache.";
132    return FILE_ERROR_FAILED;
133  }
134
135  if (metadata_storage->cache_file_scan_is_needed()) {
136    // Generate unique directory name.
137    const std::string& dest_directory_name = l10n_util::GetStringUTF8(
138        IDS_FILE_BROWSER_RECOVERED_FILES_FROM_GOOGLE_DRIVE_DIRECTORY_NAME);
139    base::FilePath dest_directory = downloads_directory.Append(
140        base::FilePath::FromUTF8Unsafe(dest_directory_name));
141    for (int uniquifier = 1; base::PathExists(dest_directory); ++uniquifier) {
142      dest_directory = downloads_directory.Append(
143          base::FilePath::FromUTF8Unsafe(dest_directory_name))
144          .InsertBeforeExtensionASCII(base::StringPrintf(" (%d)", uniquifier));
145    }
146
147    internal::ResourceMetadataStorage::RecoveredCacheInfoMap
148        recovered_cache_info;
149    metadata_storage->RecoverCacheInfoFromTrashedResourceMap(
150        &recovered_cache_info);
151
152    LOG(WARNING) << "DB could not be opened for some reasons. "
153                 << "Recovering cache files to " << dest_directory.value();
154    if (!cache->RecoverFilesFromCacheDirectory(dest_directory,
155                                               recovered_cache_info)) {
156      LOG(WARNING) << "Failed to recover cache files.";
157      return FILE_ERROR_FAILED;
158    }
159  }
160
161  FileError error = resource_metadata->Initialize();
162  LOG_IF(WARNING, error != FILE_ERROR_OK)
163      << "Failed to initialize resource metadata. " << FileErrorToString(error);
164  return error;
165}
166
167}  // namespace
168
169// Observes drive disable Preference's change.
170class DriveIntegrationService::PreferenceWatcher {
171 public:
172  explicit PreferenceWatcher(PrefService* pref_service)
173      : pref_service_(pref_service),
174        integration_service_(NULL),
175        weak_ptr_factory_(this) {
176    DCHECK(pref_service);
177    pref_change_registrar_.Init(pref_service);
178    pref_change_registrar_.Add(
179        prefs::kDisableDrive,
180        base::Bind(&PreferenceWatcher::OnPreferenceChanged,
181                   weak_ptr_factory_.GetWeakPtr()));
182  }
183
184  void set_integration_service(DriveIntegrationService* integration_service) {
185    integration_service_ = integration_service;
186  }
187
188 private:
189  void OnPreferenceChanged() {
190    DCHECK(integration_service_);
191    integration_service_->SetEnabled(
192        !pref_service_->GetBoolean(prefs::kDisableDrive));
193  }
194
195  PrefService* pref_service_;
196  PrefChangeRegistrar pref_change_registrar_;
197  DriveIntegrationService* integration_service_;
198
199  base::WeakPtrFactory<PreferenceWatcher> weak_ptr_factory_;
200  DISALLOW_COPY_AND_ASSIGN(PreferenceWatcher);
201};
202
203DriveIntegrationService::DriveIntegrationService(
204    Profile* profile,
205    PreferenceWatcher* preference_watcher,
206    DriveServiceInterface* test_drive_service,
207    const base::FilePath& test_cache_root,
208    FileSystemInterface* test_file_system)
209    : profile_(profile),
210      state_(NOT_INITIALIZED),
211      enabled_(false),
212      cache_root_directory_(!test_cache_root.empty() ?
213                            test_cache_root : util::GetCacheRootPath(profile)),
214      weak_ptr_factory_(this) {
215  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
216
217  base::SequencedWorkerPool* blocking_pool = BrowserThread::GetBlockingPool();
218  blocking_task_runner_ = blocking_pool->GetSequencedTaskRunner(
219      blocking_pool->GetSequenceToken());
220
221  ProfileOAuth2TokenService* oauth_service =
222      ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
223
224  if (test_drive_service) {
225    drive_service_.reset(test_drive_service);
226  } else if (util::IsDriveV2ApiEnabled()) {
227    drive_service_.reset(new DriveAPIService(
228        oauth_service,
229        g_browser_process->system_request_context(),
230        blocking_task_runner_.get(),
231        GURL(google_apis::DriveApiUrlGenerator::kBaseUrlForProduction),
232        GURL(google_apis::DriveApiUrlGenerator::kBaseDownloadUrlForProduction),
233        GURL(google_apis::GDataWapiUrlGenerator::kBaseUrlForProduction),
234        GetDriveUserAgent()));
235  } else {
236    drive_service_.reset(new GDataWapiService(
237        oauth_service,
238        g_browser_process->system_request_context(),
239        blocking_task_runner_.get(),
240        GURL(google_apis::GDataWapiUrlGenerator::kBaseUrlForProduction),
241        GURL(google_apis::GDataWapiUrlGenerator::kBaseDownloadUrlForProduction),
242        GetDriveUserAgent()));
243  }
244  scheduler_.reset(new JobScheduler(
245      profile_->GetPrefs(),
246      drive_service_.get(),
247      blocking_task_runner_.get()));
248  metadata_storage_.reset(new internal::ResourceMetadataStorage(
249      cache_root_directory_.Append(kMetadataDirectory),
250      blocking_task_runner_.get()));
251  cache_.reset(new internal::FileCache(
252      metadata_storage_.get(),
253      cache_root_directory_.Append(kCacheFileDirectory),
254      blocking_task_runner_.get(),
255      NULL /* free_disk_space_getter */));
256  drive_app_registry_.reset(new DriveAppRegistry(scheduler_.get()));
257
258  resource_metadata_.reset(new internal::ResourceMetadata(
259      metadata_storage_.get(), blocking_task_runner_));
260
261  file_system_.reset(
262      test_file_system ? test_file_system : new FileSystem(
263          profile_->GetPrefs(),
264          cache_.get(),
265          drive_service_.get(),
266          scheduler_.get(),
267          resource_metadata_.get(),
268          blocking_task_runner_.get(),
269          cache_root_directory_.Append(kTemporaryFileDirectory)));
270  download_handler_.reset(new DownloadHandler(file_system()));
271  debug_info_collector_.reset(
272      new DebugInfoCollector(file_system(), cache_.get(),
273                             blocking_task_runner_.get()));
274
275  if (preference_watcher) {
276    preference_watcher_.reset(preference_watcher);
277    preference_watcher->set_integration_service(this);
278  }
279
280  SetEnabled(drive::util::IsDriveEnabledForProfile(profile));
281}
282
283DriveIntegrationService::~DriveIntegrationService() {
284  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
285}
286
287void DriveIntegrationService::Shutdown() {
288  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
289
290  weak_ptr_factory_.InvalidateWeakPtrs();
291
292  DriveNotificationManager* drive_notification_manager =
293      DriveNotificationManagerFactory::GetForBrowserContext(profile_);
294  if (drive_notification_manager)
295    drive_notification_manager->RemoveObserver(this);
296
297  RemoveDriveMountPoint();
298  debug_info_collector_.reset();
299  download_handler_.reset();
300  file_system_.reset();
301  drive_app_registry_.reset();
302  scheduler_.reset();
303  drive_service_.reset();
304}
305
306void DriveIntegrationService::SetEnabled(bool enabled) {
307  // If Drive is being disabled, ensure the download destination preference to
308  // be out of Drive. Do this before "Do nothing if not changed." because we
309  // want to run the check for the first SetEnabled() called in the constructor,
310  // which may be a change from false to false.
311  if (!enabled)
312    AvoidDriveAsDownloadDirecotryPreference();
313
314  // Do nothing if not changed.
315  if (enabled_ == enabled)
316    return;
317
318  if (enabled) {
319    enabled_ = true;
320    switch (state_) {
321      case NOT_INITIALIZED:
322        // If the initialization is not yet done, trigger it.
323        Initialize();
324        return;
325
326      case INITIALIZING:
327      case REMOUNTING:
328        // If the state is INITIALIZING or REMOUNTING, at the end of the
329        // process, it tries to mounting (with re-checking enabled state).
330        // Do nothing for now.
331        return;
332
333      case INITIALIZED:
334        // The integration service is already initialized. Add the mount point.
335        AddDriveMountPoint();
336        return;
337    }
338    NOTREACHED();
339  } else {
340    RemoveDriveMountPoint();
341    enabled_ = false;
342  }
343}
344
345bool DriveIntegrationService::IsMounted() const {
346  // Look up the registered path, and just discard it.
347  // GetRegisteredPath() returns true if the path is available.
348  const base::FilePath& drive_mount_point = util::GetDriveMountPointPath();
349  base::FilePath unused;
350  return BrowserContext::GetMountPoints(profile_)->GetRegisteredPath(
351      drive_mount_point.BaseName().AsUTF8Unsafe(), &unused);
352}
353
354void DriveIntegrationService::AddObserver(
355    DriveIntegrationServiceObserver* observer) {
356  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
357  observers_.AddObserver(observer);
358}
359
360void DriveIntegrationService::RemoveObserver(
361    DriveIntegrationServiceObserver* observer) {
362  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
363  observers_.RemoveObserver(observer);
364}
365
366void DriveIntegrationService::OnNotificationReceived() {
367  file_system_->CheckForUpdates();
368  drive_app_registry_->Update();
369}
370
371void DriveIntegrationService::OnPushNotificationEnabled(bool enabled) {
372  if (enabled)
373    drive_app_registry_->Update();
374
375  const char* status = (enabled ? "enabled" : "disabled");
376  util::Log(logging::LOG_INFO, "Push notification is %s", status);
377}
378
379void DriveIntegrationService::ClearCacheAndRemountFileSystem(
380    const base::Callback<void(bool)>& callback) {
381  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
382  DCHECK(!callback.is_null());
383
384  if (state_ != INITIALIZED) {
385    callback.Run(false);
386    return;
387  }
388
389  RemoveDriveMountPoint();
390
391  state_ = REMOUNTING;
392  // Reloads the Drive app registry.
393  drive_app_registry_->Update();
394  // Reloading the file system clears resource metadata and cache.
395  file_system_->Reload(base::Bind(
396      &DriveIntegrationService::AddBackDriveMountPoint,
397      weak_ptr_factory_.GetWeakPtr(),
398      callback));
399}
400
401void DriveIntegrationService::AddBackDriveMountPoint(
402    const base::Callback<void(bool)>& callback,
403    FileError error) {
404  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
405  DCHECK(!callback.is_null());
406
407  state_ = error == FILE_ERROR_OK ? INITIALIZED : NOT_INITIALIZED;
408
409  if (error != FILE_ERROR_OK || !enabled_) {
410    // Failed to reload, or Drive was disabled during the reloading.
411    callback.Run(false);
412    return;
413  }
414
415  AddDriveMountPoint();
416  callback.Run(true);
417}
418
419void DriveIntegrationService::AddDriveMountPoint() {
420  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
421  DCHECK_EQ(INITIALIZED, state_);
422  DCHECK(enabled_);
423
424  const base::FilePath drive_mount_point = util::GetDriveMountPointPath();
425  fileapi::ExternalMountPoints* mount_points =
426      BrowserContext::GetMountPoints(profile_);
427  DCHECK(mount_points);
428
429  bool success = mount_points->RegisterFileSystem(
430      drive_mount_point.BaseName().AsUTF8Unsafe(),
431      fileapi::kFileSystemTypeDrive,
432      fileapi::FileSystemMountOption(),
433      drive_mount_point);
434
435  if (success) {
436    util::Log(logging::LOG_INFO, "Drive mount point is added");
437    FOR_EACH_OBSERVER(DriveIntegrationServiceObserver, observers_,
438                      OnFileSystemMounted());
439  }
440}
441
442void DriveIntegrationService::RemoveDriveMountPoint() {
443  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
444
445  job_list()->CancelAllJobs();
446
447  FOR_EACH_OBSERVER(DriveIntegrationServiceObserver, observers_,
448                    OnFileSystemBeingUnmounted());
449
450  fileapi::ExternalMountPoints* mount_points =
451      BrowserContext::GetMountPoints(profile_);
452  DCHECK(mount_points);
453
454  mount_points->RevokeFileSystem(
455      util::GetDriveMountPointPath().BaseName().AsUTF8Unsafe());
456  util::Log(logging::LOG_INFO, "Drive mount point is removed");
457}
458
459void DriveIntegrationService::Initialize() {
460  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
461  DCHECK_EQ(NOT_INITIALIZED, state_);
462  DCHECK(enabled_);
463
464  state_ = INITIALIZING;
465
466  base::PostTaskAndReplyWithResult(
467      blocking_task_runner_.get(),
468      FROM_HERE,
469      base::Bind(&InitializeMetadata,
470                 cache_root_directory_,
471                 metadata_storage_.get(),
472                 cache_.get(),
473                 resource_metadata_.get(),
474                 drive_service_->GetResourceIdCanonicalizer(),
475                 file_manager::util::GetDownloadsFolderForProfile(profile_)),
476      base::Bind(&DriveIntegrationService::InitializeAfterMetadataInitialized,
477                 weak_ptr_factory_.GetWeakPtr()));
478}
479
480void DriveIntegrationService::InitializeAfterMetadataInitialized(
481    FileError error) {
482  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
483  DCHECK_EQ(INITIALIZING, state_);
484
485  drive_service_->Initialize(
486      ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->
487          GetPrimaryAccountId());
488
489  if (error != FILE_ERROR_OK) {
490    LOG(WARNING) << "Failed to initialize: " << FileErrorToString(error);
491
492    // Cannot used Drive. Set the download destination preference out of Drive.
493    AvoidDriveAsDownloadDirecotryPreference();
494
495    // Back to NOT_INITIALIZED state. Then, re-running Initialize() should
496    // work if the error is recoverable manually (such as out of disk space).
497    state_ = NOT_INITIALIZED;
498    return;
499  }
500
501  content::DownloadManager* download_manager =
502      g_browser_process->download_status_updater() ?
503      BrowserContext::GetDownloadManager(profile_) : NULL;
504  download_handler_->Initialize(
505      download_manager,
506      cache_root_directory_.Append(kTemporaryFileDirectory));
507
508  // Register for Google Drive invalidation notifications.
509  DriveNotificationManager* drive_notification_manager =
510      DriveNotificationManagerFactory::GetForBrowserContext(profile_);
511  if (drive_notification_manager) {
512    drive_notification_manager->AddObserver(this);
513    const bool registered =
514        drive_notification_manager->push_notification_registered();
515    const char* status = (registered ? "registered" : "not registered");
516    util::Log(logging::LOG_INFO, "Push notification is %s", status);
517
518    if (drive_notification_manager->push_notification_enabled())
519      drive_app_registry_->Update();
520  }
521
522  state_ = INITIALIZED;
523
524  // Mount only when the drive is enabled. Initialize is triggered by
525  // SetEnabled(true), but there is a change to disable it again during
526  // the metadata initialization, so we need to look this up again here.
527  if (enabled_)
528    AddDriveMountPoint();
529}
530
531void DriveIntegrationService::AvoidDriveAsDownloadDirecotryPreference() {
532  PrefService* pref_service = profile_->GetPrefs();
533  if (util::IsUnderDriveMountPoint(
534          pref_service->GetFilePath(prefs::kDownloadDefaultDirectory))) {
535    pref_service->SetFilePath(
536        prefs::kDownloadDefaultDirectory,
537        file_manager::util::GetDownloadsFolderForProfile(profile_));
538  }
539}
540
541//===================== DriveIntegrationServiceFactory =======================
542
543// static
544DriveIntegrationService* DriveIntegrationServiceFactory::GetForProfile(
545    Profile* profile) {
546  return GetForProfileRegardlessOfStates(profile);
547}
548
549// static
550DriveIntegrationService*
551DriveIntegrationServiceFactory::GetForProfileRegardlessOfStates(
552    Profile* profile) {
553  return static_cast<DriveIntegrationService*>(
554      GetInstance()->GetServiceForBrowserContext(profile, true));
555}
556
557// static
558DriveIntegrationService* DriveIntegrationServiceFactory::FindForProfile(
559    Profile* profile) {
560  return FindForProfileRegardlessOfStates(profile);
561}
562
563// static
564DriveIntegrationService*
565DriveIntegrationServiceFactory::FindForProfileRegardlessOfStates(
566    Profile* profile) {
567  return static_cast<DriveIntegrationService*>(
568      GetInstance()->GetServiceForBrowserContext(profile, false));
569}
570
571// static
572DriveIntegrationServiceFactory* DriveIntegrationServiceFactory::GetInstance() {
573  return Singleton<DriveIntegrationServiceFactory>::get();
574}
575
576// static
577void DriveIntegrationServiceFactory::SetFactoryForTest(
578    const FactoryCallback& factory_for_test) {
579  GetInstance()->factory_for_test_ = factory_for_test;
580}
581
582DriveIntegrationServiceFactory::DriveIntegrationServiceFactory()
583    : BrowserContextKeyedServiceFactory(
584        "DriveIntegrationService",
585        BrowserContextDependencyManager::GetInstance()) {
586  DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
587  DependsOn(DriveNotificationManagerFactory::GetInstance());
588  DependsOn(DownloadServiceFactory::GetInstance());
589}
590
591DriveIntegrationServiceFactory::~DriveIntegrationServiceFactory() {
592}
593
594BrowserContextKeyedService*
595DriveIntegrationServiceFactory::BuildServiceInstanceFor(
596    content::BrowserContext* context) const {
597  Profile* profile = Profile::FromBrowserContext(context);
598
599  DriveIntegrationService* service = NULL;
600  if (factory_for_test_.is_null()) {
601    DriveIntegrationService::PreferenceWatcher* preference_watcher = NULL;
602    if (chromeos::IsProfileAssociatedWithGaiaAccount(profile)) {
603      // Drive File System can be enabled.
604      preference_watcher =
605          new DriveIntegrationService::PreferenceWatcher(profile->GetPrefs());
606    }
607
608    service = new DriveIntegrationService(profile, preference_watcher,
609                                          NULL, base::FilePath(), NULL);
610  } else {
611    service = factory_for_test_.Run(profile);
612  }
613
614  return service;
615}
616
617}  // namespace drive
618