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