Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / file_manager / volume_manager.cc
1 // Copyright 2013 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/file_manager/volume_manager.h"
6
7 #include "base/basictypes.h"
8 #include "base/bind.h"
9 #include "base/command_line.h"
10 #include "base/files/file_path.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
17 #include "chrome/browser/chromeos/drive/file_system_interface.h"
18 #include "chrome/browser/chromeos/drive/file_system_util.h"
19 #include "chrome/browser/chromeos/file_manager/mounted_disk_monitor.h"
20 #include "chrome/browser/chromeos/file_manager/path_util.h"
21 #include "chrome/browser/chromeos/file_manager/snapshot_manager.h"
22 #include "chrome/browser/chromeos/file_manager/volume_manager_factory.h"
23 #include "chrome/browser/chromeos/file_manager/volume_manager_observer.h"
24 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
25 #include "chrome/browser/chromeos/profiles/profile_helper.h"
26 #include "chrome/browser/local_discovery/storage/privet_filesystem_constants.h"
27 #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h"
28 #include "chrome/browser/profiles/profile.h"
29 #include "chrome/common/chrome_switches.h"
30 #include "chrome/common/pref_names.h"
31 #include "chromeos/chromeos_switches.h"
32 #include "chromeos/disks/disk_mount_manager.h"
33 #include "components/storage_monitor/storage_monitor.h"
34 #include "content/public/browser/browser_context.h"
35 #include "content/public/browser/browser_thread.h"
36 #include "webkit/browser/fileapi/external_mount_points.h"
37
38 namespace file_manager {
39 namespace {
40
41 // A named constant to be passed to the |is_remounting| parameter.
42 const bool kNotRemounting = false;
43
44 const char kFileManagerMTPMountNamePrefix[] = "fileman-mtp-";
45 const char kMtpVolumeIdPrefix [] = "mtp:";
46
47 // Registers |path| as the "Downloads" folder to the FileSystem API backend.
48 // If another folder is already mounted. It revokes and overrides the old one.
49 bool RegisterDownloadsMountPoint(Profile* profile, const base::FilePath& path) {
50   // Although we show only profile's own "Downloads" folder in Files.app,
51   // in the backend we need to mount all profile's download directory globally.
52   // Otherwise, Files.app cannot support cross-profile file copies, etc.
53   // For this reason, we need to register to the global GetSystemInstance().
54   const std::string mount_point_name =
55       file_manager::util::GetDownloadsMountPointName(profile);
56   fileapi::ExternalMountPoints* const mount_points =
57       fileapi::ExternalMountPoints::GetSystemInstance();
58
59   // In some tests we want to override existing Downloads mount point, so we
60   // first revoke the existing mount point (if any).
61   mount_points->RevokeFileSystem(mount_point_name);
62   return mount_points->RegisterFileSystem(
63       mount_point_name, fileapi::kFileSystemTypeNativeLocal,
64       fileapi::FileSystemMountOption(), path);
65 }
66
67 // Finds the path register as the "Downloads" folder to FileSystem API backend.
68 // Returns false if it is not registered.
69 bool FindDownloadsMountPointPath(Profile* profile, base::FilePath* path) {
70   const std::string mount_point_name =
71       util::GetDownloadsMountPointName(profile);
72   fileapi::ExternalMountPoints* const mount_points =
73       fileapi::ExternalMountPoints::GetSystemInstance();
74
75   return mount_points->GetRegisteredPath(mount_point_name, path);
76 }
77
78 VolumeType MountTypeToVolumeType(chromeos::MountType type) {
79   switch (type) {
80     case chromeos::MOUNT_TYPE_INVALID:
81       // We don't expect this value, but list here, so that when any value
82       // is added to the enum definition but this is not edited, the compiler
83       // warns it.
84       break;
85     case chromeos::MOUNT_TYPE_DEVICE:
86       return VOLUME_TYPE_REMOVABLE_DISK_PARTITION;
87     case chromeos::MOUNT_TYPE_ARCHIVE:
88       return VOLUME_TYPE_MOUNTED_ARCHIVE_FILE;
89   }
90
91   NOTREACHED();
92   return VOLUME_TYPE_DOWNLOADS_DIRECTORY;
93 }
94
95 // Returns a string representation of the given volume type.
96 std::string VolumeTypeToString(VolumeType type) {
97   switch (type) {
98     case VOLUME_TYPE_GOOGLE_DRIVE:
99       return "drive";
100     case VOLUME_TYPE_DOWNLOADS_DIRECTORY:
101       return "downloads";
102     case VOLUME_TYPE_REMOVABLE_DISK_PARTITION:
103       return "removable";
104     case VOLUME_TYPE_MOUNTED_ARCHIVE_FILE:
105       return "archive";
106     case VOLUME_TYPE_CLOUD_DEVICE:
107       return "cloud_device";
108     case VOLUME_TYPE_PROVIDED:
109       return "provided";
110     case VOLUME_TYPE_MTP:
111       return "mtp";
112     case VOLUME_TYPE_TESTING:
113       return "testing";
114     case NUM_VOLUME_TYPE:
115       break;
116   }
117   NOTREACHED();
118   return "";
119 }
120
121 // Generates a unique volume ID for the given volume info.
122 std::string GenerateVolumeId(const VolumeInfo& volume_info) {
123   // For the same volume type, base names are unique, as mount points are
124   // flat for the same volume type.
125   return (VolumeTypeToString(volume_info.type) + ":" +
126           volume_info.mount_path.BaseName().AsUTF8Unsafe());
127 }
128
129 // Returns the VolumeInfo for Drive file system.
130 VolumeInfo CreateDriveVolumeInfo(Profile* profile) {
131   const base::FilePath& drive_path =
132       drive::util::GetDriveMountPointPath(profile);
133
134   VolumeInfo volume_info;
135   volume_info.type = VOLUME_TYPE_GOOGLE_DRIVE;
136   volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN;
137   volume_info.source_path = drive_path;
138   volume_info.mount_path = drive_path;
139   volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
140   volume_info.is_parent = false;
141   volume_info.is_read_only = false;
142   volume_info.volume_id = GenerateVolumeId(volume_info);
143   return volume_info;
144 }
145
146 VolumeInfo CreateDownloadsVolumeInfo(const base::FilePath& downloads_path) {
147   VolumeInfo volume_info;
148   volume_info.type = VOLUME_TYPE_DOWNLOADS_DIRECTORY;
149   volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN;
150   // Keep source_path empty.
151   volume_info.mount_path = downloads_path;
152   volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
153   volume_info.is_parent = false;
154   volume_info.is_read_only = false;
155   volume_info.volume_id = GenerateVolumeId(volume_info);
156   return volume_info;
157 }
158
159 VolumeInfo CreateTestingVolumeInfo(const base::FilePath& path,
160                                    VolumeType volume_type,
161                                    chromeos::DeviceType device_type) {
162   VolumeInfo volume_info;
163   volume_info.type = volume_type;
164   volume_info.device_type = device_type;
165   // Keep source_path empty.
166   volume_info.mount_path = path;
167   volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
168   volume_info.is_parent = false;
169   volume_info.is_read_only = false;
170   volume_info.volume_id = GenerateVolumeId(volume_info);
171   return volume_info;
172 }
173
174 VolumeInfo CreateVolumeInfoFromMountPointInfo(
175     const chromeos::disks::DiskMountManager::MountPointInfo& mount_point,
176     const chromeos::disks::DiskMountManager::Disk* disk) {
177   VolumeInfo volume_info;
178   volume_info.type = MountTypeToVolumeType(mount_point.mount_type);
179   volume_info.source_path = base::FilePath(mount_point.source_path);
180   volume_info.mount_path = base::FilePath(mount_point.mount_path);
181   volume_info.mount_condition = mount_point.mount_condition;
182   volume_info.volume_label = volume_info.mount_path.BaseName().AsUTF8Unsafe();
183   if (disk) {
184     volume_info.device_type = disk->device_type();
185     volume_info.system_path_prefix =
186         base::FilePath(disk->system_path_prefix());
187     volume_info.is_parent = disk->is_parent();
188     volume_info.is_read_only = disk->is_read_only();
189   } else {
190     volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN;
191     volume_info.is_parent = false;
192     volume_info.is_read_only =
193         (mount_point.mount_type == chromeos::MOUNT_TYPE_ARCHIVE);
194   }
195   volume_info.volume_id = GenerateVolumeId(volume_info);
196
197   return volume_info;
198 }
199
200 VolumeInfo CreatePrivetVolumeInfo(
201     const local_discovery::PrivetVolumeLister::VolumeInfo& privet_volume_info) {
202   VolumeInfo volume_info;
203   volume_info.type = VOLUME_TYPE_CLOUD_DEVICE;
204   volume_info.mount_path = privet_volume_info.volume_path;
205   volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
206   volume_info.is_parent = true;
207   volume_info.is_read_only = true;
208   volume_info.volume_id = GenerateVolumeId(volume_info);
209   return volume_info;
210 }
211
212 VolumeInfo CreateProvidedFileSystemVolumeInfo(
213     const chromeos::file_system_provider::ProvidedFileSystemInfo&
214         file_system_info) {
215   VolumeInfo volume_info;
216   volume_info.file_system_id = file_system_info.file_system_id();
217   volume_info.extension_id = file_system_info.extension_id();
218   volume_info.volume_label = file_system_info.display_name();
219   volume_info.type = VOLUME_TYPE_PROVIDED;
220   volume_info.mount_path = file_system_info.mount_path();
221   volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
222   volume_info.is_parent = true;
223   volume_info.is_read_only = !file_system_info.writable();
224   volume_info.volume_id = GenerateVolumeId(volume_info);
225   return volume_info;
226 }
227
228 std::string GetMountPointNameForMediaStorage(
229     const storage_monitor::StorageInfo& info) {
230   std::string name(kFileManagerMTPMountNamePrefix);
231   name += info.device_id();
232   return name;
233 }
234
235 }  // namespace
236
237 VolumeInfo::VolumeInfo()
238     : type(VOLUME_TYPE_GOOGLE_DRIVE),
239       device_type(chromeos::DEVICE_TYPE_UNKNOWN),
240       mount_condition(chromeos::disks::MOUNT_CONDITION_NONE),
241       is_parent(false),
242       is_read_only(false) {
243 }
244
245 VolumeInfo::~VolumeInfo() {
246 }
247
248 VolumeManager::VolumeManager(
249     Profile* profile,
250     drive::DriveIntegrationService* drive_integration_service,
251     chromeos::PowerManagerClient* power_manager_client,
252     chromeos::disks::DiskMountManager* disk_mount_manager,
253     chromeos::file_system_provider::Service* file_system_provider_service)
254     : profile_(profile),
255       drive_integration_service_(drive_integration_service),
256       disk_mount_manager_(disk_mount_manager),
257       mounted_disk_monitor_(new MountedDiskMonitor(power_manager_client)),
258       file_system_provider_service_(file_system_provider_service),
259       snapshot_manager_(new SnapshotManager(profile_)),
260       weak_ptr_factory_(this) {
261   DCHECK(disk_mount_manager);
262 }
263
264 VolumeManager::~VolumeManager() {
265 }
266
267 VolumeManager* VolumeManager::Get(content::BrowserContext* context) {
268   return VolumeManagerFactory::Get(context);
269 }
270
271 void VolumeManager::Initialize() {
272   // If in Sign in profile, then skip mounting and listening for mount events.
273   if (chromeos::ProfileHelper::IsSigninProfile(profile_))
274     return;
275
276   // Path to mount user folders have changed several times. We need to migrate
277   // the old preferences on paths to the new format when needed. For the detail,
278   // see the comments in file_manager::util::MigratePathFromOldFormat,
279   // Note: Preferences related to downloads are handled in download_prefs.cc.
280   // TODO(kinaba): Remove this after several rounds of releases.
281   const base::FilePath old_path =
282       profile_->GetPrefs()->GetFilePath(prefs::kSelectFileLastDirectory);
283   base::FilePath new_path;
284   if (!old_path.empty() &&
285       file_manager::util::MigratePathFromOldFormat(profile_,
286                                                    old_path, &new_path)) {
287     profile_->GetPrefs()->SetFilePath(prefs::kSelectFileLastDirectory,
288                                       new_path);
289   }
290
291   // Register 'Downloads' folder for the profile to the file system.
292   const base::FilePath downloads =
293       file_manager::util::GetDownloadsFolderForProfile(profile_);
294   const bool success = RegisterDownloadsMountPoint(profile_, downloads);
295   DCHECK(success);
296
297   DoMountEvent(chromeos::MOUNT_ERROR_NONE,
298                CreateDownloadsVolumeInfo(downloads),
299                kNotRemounting);
300
301   // Subscribe to DriveIntegrationService.
302   if (drive_integration_service_) {
303     drive_integration_service_->AddObserver(this);
304     if (drive_integration_service_->IsMounted()) {
305       DoMountEvent(chromeos::MOUNT_ERROR_NONE,
306                    CreateDriveVolumeInfo(profile_),
307                    kNotRemounting);
308     }
309   }
310
311   // Subscribe to DiskMountManager.
312   disk_mount_manager_->AddObserver(this);
313   disk_mount_manager_->EnsureMountInfoRefreshed(
314       base::Bind(&VolumeManager::OnDiskMountManagerRefreshed,
315                  weak_ptr_factory_.GetWeakPtr()));
316
317   // Subscribe to FileSystemProviderService and register currently mounted
318   // volumes for the profile.
319   if (file_system_provider_service_) {
320     using chromeos::file_system_provider::ProvidedFileSystemInfo;
321     file_system_provider_service_->AddObserver(this);
322
323     std::vector<ProvidedFileSystemInfo> file_system_info_list =
324         file_system_provider_service_->GetProvidedFileSystemInfoList();
325     for (size_t i = 0; i < file_system_info_list.size(); ++i) {
326       VolumeInfo volume_info =
327           CreateProvidedFileSystemVolumeInfo(file_system_info_list[i]);
328       DoMountEvent(chromeos::MOUNT_ERROR_NONE, volume_info, kNotRemounting);
329     }
330   }
331
332   // Subscribe to Profile Preference change.
333   pref_change_registrar_.Init(profile_->GetPrefs());
334   pref_change_registrar_.Add(
335       prefs::kExternalStorageDisabled,
336       base::Bind(&VolumeManager::OnExternalStorageDisabledChanged,
337                  weak_ptr_factory_.GetWeakPtr()));
338
339   // Subscribe to Privet volume lister.
340   if (CommandLine::ForCurrentProcess()->HasSwitch(
341           switches::kEnablePrivetStorage)) {
342     privet_volume_lister_.reset(new local_discovery::PrivetVolumeLister(
343         base::Bind(&VolumeManager::OnPrivetVolumesAvailable,
344                    weak_ptr_factory_.GetWeakPtr())));
345     privet_volume_lister_->Start();
346   }
347
348   // Subscribe to storage monitor for MTP notifications.
349   const bool disable_mtp =
350       CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
351           chromeos::switches::kEnableFileManagerMTP) == "false";
352   if (!disable_mtp && storage_monitor::StorageMonitor::GetInstance()) {
353     storage_monitor::StorageMonitor::GetInstance()->EnsureInitialized(
354         base::Bind(&VolumeManager::OnStorageMonitorInitialized,
355                    weak_ptr_factory_.GetWeakPtr()));
356   }
357 }
358
359 void VolumeManager::Shutdown() {
360   weak_ptr_factory_.InvalidateWeakPtrs();
361
362   snapshot_manager_.reset();
363   pref_change_registrar_.RemoveAll();
364   disk_mount_manager_->RemoveObserver(this);
365   if (storage_monitor::StorageMonitor::GetInstance())
366     storage_monitor::StorageMonitor::GetInstance()->RemoveObserver(this);
367
368   if (drive_integration_service_)
369     drive_integration_service_->RemoveObserver(this);
370
371   if (file_system_provider_service_)
372     file_system_provider_service_->RemoveObserver(this);
373 }
374
375 void VolumeManager::AddObserver(VolumeManagerObserver* observer) {
376   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
377   DCHECK(observer);
378   observers_.AddObserver(observer);
379 }
380
381 void VolumeManager::RemoveObserver(VolumeManagerObserver* observer) {
382   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
383   DCHECK(observer);
384   observers_.RemoveObserver(observer);
385 }
386
387 std::vector<VolumeInfo> VolumeManager::GetVolumeInfoList() const {
388   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
389
390   std::vector<VolumeInfo> result;
391   for (std::map<std::string, VolumeInfo>::const_iterator iter =
392            mounted_volumes_.begin();
393        iter != mounted_volumes_.end();
394        ++iter) {
395     result.push_back(iter->second);
396   }
397   return result;
398 }
399
400 bool VolumeManager::FindVolumeInfoById(const std::string& volume_id,
401                                        VolumeInfo* result) const {
402   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
403   DCHECK(result);
404
405   std::map<std::string, VolumeInfo>::const_iterator iter =
406       mounted_volumes_.find(volume_id);
407   if (iter == mounted_volumes_.end())
408     return false;
409   *result = iter->second;
410   return true;
411 }
412
413 bool VolumeManager::RegisterDownloadsDirectoryForTesting(
414     const base::FilePath& path) {
415   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
416
417   base::FilePath old_path;
418   if (FindDownloadsMountPointPath(profile_, &old_path)) {
419     DoUnmountEvent(chromeos::MOUNT_ERROR_NONE,
420                    CreateDownloadsVolumeInfo(old_path));
421   }
422
423   bool success = RegisterDownloadsMountPoint(profile_, path);
424   DoMountEvent(
425       success ? chromeos::MOUNT_ERROR_NONE : chromeos::MOUNT_ERROR_INVALID_PATH,
426       CreateDownloadsVolumeInfo(path),
427       kNotRemounting);
428   return success;
429 }
430
431 void VolumeManager::AddVolumeInfoForTesting(const base::FilePath& path,
432                                             VolumeType volume_type,
433                                             chromeos::DeviceType device_type) {
434   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
435   DoMountEvent(chromeos::MOUNT_ERROR_NONE,
436                CreateTestingVolumeInfo(path, volume_type, device_type),
437                kNotRemounting);
438 }
439
440 void VolumeManager::OnFileSystemMounted() {
441   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
442
443   // Raise mount event.
444   // We can pass chromeos::MOUNT_ERROR_NONE even when authentication is failed
445   // or network is unreachable. These two errors will be handled later.
446   VolumeInfo volume_info = CreateDriveVolumeInfo(profile_);
447   DoMountEvent(chromeos::MOUNT_ERROR_NONE, volume_info, kNotRemounting);
448 }
449
450 void VolumeManager::OnFileSystemBeingUnmounted() {
451   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
452
453   VolumeInfo volume_info = CreateDriveVolumeInfo(profile_);
454   DoUnmountEvent(chromeos::MOUNT_ERROR_NONE, volume_info);
455 }
456
457 void VolumeManager::OnDiskEvent(
458     chromeos::disks::DiskMountManager::DiskEvent event,
459     const chromeos::disks::DiskMountManager::Disk* disk) {
460   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
461
462   mounted_disk_monitor_->OnDiskEvent(event, disk);
463
464   // Disregard hidden devices.
465   if (disk->is_hidden())
466     return;
467
468   switch (event) {
469     case chromeos::disks::DiskMountManager::DISK_ADDED:
470     case chromeos::disks::DiskMountManager::DISK_CHANGED: {
471       if (disk->device_path().empty()) {
472         DVLOG(1) << "Empty system path for " << disk->device_path();
473         return;
474       }
475
476       bool mounting = false;
477       if (disk->mount_path().empty() && disk->has_media() &&
478           !profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
479         // If disk is not mounted yet and it has media and there is no policy
480         // forbidding external storage, give it a try.
481         // Initiate disk mount operation. MountPath auto-detects the filesystem
482         // format if the second argument is empty. The third argument (mount
483         // label) is not used in a disk mount operation.
484         disk_mount_manager_->MountPath(
485             disk->device_path(), std::string(), std::string(),
486             chromeos::MOUNT_TYPE_DEVICE);
487         mounting = true;
488       }
489
490       // Notify to observers.
491       FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
492                         OnDiskAdded(*disk, mounting));
493       return;
494     }
495
496     case chromeos::disks::DiskMountManager::DISK_REMOVED:
497       // If the disk is already mounted, unmount it.
498       if (!disk->mount_path().empty()) {
499         disk_mount_manager_->UnmountPath(
500             disk->mount_path(),
501             chromeos::UNMOUNT_OPTIONS_LAZY,
502             chromeos::disks::DiskMountManager::UnmountPathCallback());
503       }
504
505       // Notify to observers.
506       FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
507                         OnDiskRemoved(*disk));
508       const std::string& device_path = disk->system_path_prefix();
509       if (mounted_disk_monitor_->DeviceIsHardUnpluggedButNotReported(
510               device_path)) {
511         FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
512                           OnHardUnplugged(device_path));
513         mounted_disk_monitor_->MarkAsHardUnpluggedReported(device_path);
514       }
515       return;
516   }
517   NOTREACHED();
518 }
519
520 void VolumeManager::OnDeviceEvent(
521     chromeos::disks::DiskMountManager::DeviceEvent event,
522     const std::string& device_path) {
523   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
524
525   mounted_disk_monitor_->OnDeviceEvent(event, device_path);
526
527   DVLOG(1) << "OnDeviceEvent: " << event << ", " << device_path;
528   switch (event) {
529     case chromeos::disks::DiskMountManager::DEVICE_ADDED:
530       FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
531                         OnDeviceAdded(device_path));
532       return;
533     case chromeos::disks::DiskMountManager::DEVICE_REMOVED: {
534       FOR_EACH_OBSERVER(
535           VolumeManagerObserver, observers_, OnDeviceRemoved(device_path));
536       return;
537     }
538     case chromeos::disks::DiskMountManager::DEVICE_SCANNED:
539       DVLOG(1) << "Ignore SCANNED event: " << device_path;
540       return;
541   }
542   NOTREACHED();
543 }
544
545 void VolumeManager::OnMountEvent(
546     chromeos::disks::DiskMountManager::MountEvent event,
547     chromeos::MountError error_code,
548     const chromeos::disks::DiskMountManager::MountPointInfo& mount_info) {
549   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
550   DCHECK_NE(chromeos::MOUNT_TYPE_INVALID, mount_info.mount_type);
551
552   const chromeos::disks::DiskMountManager::Disk* disk =
553       disk_mount_manager_->FindDiskBySourcePath(mount_info.source_path);
554   mounted_disk_monitor_->OnMountEvent(event, error_code, mount_info, disk);
555
556   if (mount_info.mount_type == chromeos::MOUNT_TYPE_ARCHIVE) {
557     // If the file is not mounted now, tell it to drive file system so that
558     // it can handle file caching correctly.
559     // Note that drive file system knows if the file is managed by drive file
560     // system or not, so here we report all paths.
561     if ((event == chromeos::disks::DiskMountManager::MOUNTING &&
562          error_code != chromeos::MOUNT_ERROR_NONE) ||
563         (event == chromeos::disks::DiskMountManager::UNMOUNTING &&
564          error_code == chromeos::MOUNT_ERROR_NONE)) {
565       drive::FileSystemInterface* file_system =
566           drive::util::GetFileSystemByProfile(profile_);
567       if (file_system) {
568         file_system->MarkCacheFileAsUnmounted(
569             base::FilePath(mount_info.source_path),
570             base::Bind(&drive::util::EmptyFileOperationCallback));
571       }
572     }
573   }
574
575   // Notify a mounting/unmounting event to observers.
576   VolumeInfo volume_info =
577       CreateVolumeInfoFromMountPointInfo(mount_info, disk);
578   switch (event) {
579     case chromeos::disks::DiskMountManager::MOUNTING: {
580       bool is_remounting =
581           disk && mounted_disk_monitor_->DiskIsRemounting(*disk);
582       DoMountEvent(error_code, volume_info, is_remounting);
583       return;
584     }
585     case chromeos::disks::DiskMountManager::UNMOUNTING:
586       DoUnmountEvent(error_code, volume_info);
587       return;
588   }
589   NOTREACHED();
590 }
591
592 void VolumeManager::OnFormatEvent(
593     chromeos::disks::DiskMountManager::FormatEvent event,
594     chromeos::FormatError error_code,
595     const std::string& device_path) {
596   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
597   DVLOG(1) << "OnDeviceEvent: " << event << ", " << error_code
598            << ", " << device_path;
599
600   switch (event) {
601     case chromeos::disks::DiskMountManager::FORMAT_STARTED:
602       FOR_EACH_OBSERVER(
603           VolumeManagerObserver, observers_,
604           OnFormatStarted(device_path,
605                           error_code == chromeos::FORMAT_ERROR_NONE));
606       return;
607     case chromeos::disks::DiskMountManager::FORMAT_COMPLETED:
608       if (error_code == chromeos::FORMAT_ERROR_NONE) {
609         // If format is completed successfully, try to mount the device.
610         // MountPath auto-detects filesystem format if second argument is
611         // empty. The third argument (mount label) is not used in a disk mount
612         // operation.
613         disk_mount_manager_->MountPath(
614             device_path, std::string(), std::string(),
615             chromeos::MOUNT_TYPE_DEVICE);
616       }
617
618       FOR_EACH_OBSERVER(
619           VolumeManagerObserver, observers_,
620           OnFormatCompleted(device_path,
621                             error_code == chromeos::FORMAT_ERROR_NONE));
622
623       return;
624   }
625   NOTREACHED();
626 }
627
628 void VolumeManager::OnProvidedFileSystemMount(
629     const chromeos::file_system_provider::ProvidedFileSystemInfo&
630         file_system_info,
631     base::File::Error error) {
632   VolumeInfo volume_info = CreateProvidedFileSystemVolumeInfo(file_system_info);
633   // TODO(mtomasz): Introduce own type, and avoid using MountError internally,
634   // since it is related to cros disks only.
635   const chromeos::MountError mount_error = error == base::File::FILE_OK
636                                                ? chromeos::MOUNT_ERROR_NONE
637                                                : chromeos::MOUNT_ERROR_UNKNOWN;
638   DoMountEvent(mount_error, volume_info, kNotRemounting);
639 }
640
641 void VolumeManager::OnProvidedFileSystemUnmount(
642     const chromeos::file_system_provider::ProvidedFileSystemInfo&
643         file_system_info,
644     base::File::Error error) {
645   // TODO(mtomasz): Introduce own type, and avoid using MountError internally,
646   // since it is related to cros disks only.
647   const chromeos::MountError mount_error = error == base::File::FILE_OK
648                                                ? chromeos::MOUNT_ERROR_NONE
649                                                : chromeos::MOUNT_ERROR_UNKNOWN;
650   VolumeInfo volume_info = CreateProvidedFileSystemVolumeInfo(file_system_info);
651   DoUnmountEvent(mount_error, volume_info);
652 }
653
654 void VolumeManager::OnExternalStorageDisabledChanged() {
655   // If the policy just got disabled we have to unmount every device currently
656   // mounted. The opposite is fine - we can let the user re-plug her device to
657   // make it available.
658   if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
659     // We do not iterate on mount_points directly, because mount_points can
660     // be changed by UnmountPath().
661     // TODO(hidehiko): Is it necessary to unmount mounted archives, too, here?
662     while (!disk_mount_manager_->mount_points().empty()) {
663       std::string mount_path =
664           disk_mount_manager_->mount_points().begin()->second.mount_path;
665       disk_mount_manager_->UnmountPath(
666           mount_path,
667           chromeos::UNMOUNT_OPTIONS_NONE,
668           chromeos::disks::DiskMountManager::UnmountPathCallback());
669     }
670   }
671 }
672
673 void VolumeManager::OnPrivetVolumesAvailable(
674     const local_discovery::PrivetVolumeLister::VolumeList& volumes) {
675   for (local_discovery::PrivetVolumeLister::VolumeList::const_iterator i =
676            volumes.begin(); i != volumes.end(); i++) {
677     VolumeInfo volume_info = CreatePrivetVolumeInfo(*i);
678     DoMountEvent(chromeos::MOUNT_ERROR_NONE, volume_info, false);
679   }
680 }
681
682 void VolumeManager::OnRemovableStorageAttached(
683     const storage_monitor::StorageInfo& info) {
684   if (!storage_monitor::StorageInfo::IsMTPDevice(info.device_id()))
685     return;
686   if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled))
687     return;
688
689   const base::FilePath path = base::FilePath::FromUTF8Unsafe(info.location());
690   const std::string fsid = GetMountPointNameForMediaStorage(info);
691   const std::string base_name = base::UTF16ToUTF8(info.model_name());
692
693   // Assign a fresh volume ID based on the volume name.
694   std::string label = base_name;
695   for (int i = 2; mounted_volumes_.count(kMtpVolumeIdPrefix + label); ++i)
696     label = base_name + base::StringPrintf(" (%d)", i);
697
698   bool result =
699       fileapi::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
700           fsid, fileapi::kFileSystemTypeDeviceMediaAsFileStorage,
701           fileapi::FileSystemMountOption(), path);
702   DCHECK(result);
703   content::BrowserThread::PostTask(
704       content::BrowserThread::IO, FROM_HERE, base::Bind(
705           &MTPDeviceMapService::RegisterMTPFileSystem,
706           base::Unretained(MTPDeviceMapService::GetInstance()),
707           info.location(), fsid));
708
709   VolumeInfo volume_info;
710   volume_info.type = VOLUME_TYPE_MTP;
711   volume_info.mount_path = path;
712   volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
713   volume_info.is_parent = true;
714   volume_info.is_read_only = true;
715   volume_info.volume_id = kMtpVolumeIdPrefix + label;
716   volume_info.volume_label = label;
717   volume_info.source_path = path;
718   volume_info.device_type = chromeos::DEVICE_TYPE_MOBILE;
719   DoMountEvent(chromeos::MOUNT_ERROR_NONE, volume_info, false);
720 }
721
722 void VolumeManager::OnRemovableStorageDetached(
723     const storage_monitor::StorageInfo& info) {
724   if (!storage_monitor::StorageInfo::IsMTPDevice(info.device_id()))
725     return;
726
727   for (std::map<std::string, VolumeInfo>::iterator it =
728            mounted_volumes_.begin(); it != mounted_volumes_.end(); ++it) {
729     if (it->second.source_path.value() == info.location()) {
730       DoUnmountEvent(chromeos::MOUNT_ERROR_NONE, VolumeInfo(it->second));
731
732       const std::string fsid = GetMountPointNameForMediaStorage(info);
733       fileapi::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
734           fsid);
735       content::BrowserThread::PostTask(
736           content::BrowserThread::IO, FROM_HERE, base::Bind(
737               &MTPDeviceMapService::RevokeMTPFileSystem,
738               base::Unretained(MTPDeviceMapService::GetInstance()),
739               fsid));
740       return;
741     }
742   }
743 }
744
745 void VolumeManager::OnDiskMountManagerRefreshed(bool success) {
746   if (!success) {
747     LOG(ERROR) << "Failed to refresh disk mount manager";
748     return;
749   }
750
751   std::vector<VolumeInfo> archives;
752
753   const chromeos::disks::DiskMountManager::MountPointMap& mount_points =
754       disk_mount_manager_->mount_points();
755   for (chromeos::disks::DiskMountManager::MountPointMap::const_iterator it =
756            mount_points.begin();
757        it != mount_points.end();
758        ++it) {
759     if (it->second.mount_type == chromeos::MOUNT_TYPE_ARCHIVE) {
760       // Archives are mounted after other types of volume. See below.
761       archives.push_back(CreateVolumeInfoFromMountPointInfo(it->second, NULL));
762       continue;
763     }
764     DoMountEvent(
765         chromeos::MOUNT_ERROR_NONE,
766         CreateVolumeInfoFromMountPointInfo(
767             it->second,
768             disk_mount_manager_->FindDiskBySourcePath(it->second.source_path)),
769             kNotRemounting);
770   }
771
772   // We mount archives only if they are opened from currently mounted volumes.
773   // To check the condition correctly in DoMountEvent, we care about the order.
774   std::vector<bool> done(archives.size(), false);
775   for (size_t i = 0; i < archives.size(); ++i) {
776     if (done[i])
777       continue;
778
779     std::vector<VolumeInfo> chain;
780     done[i] = true;
781     chain.push_back(archives[i]);
782
783     // If archives[i]'s source_path is in another archive, mount it first.
784     for (size_t parent = i + 1; parent < archives.size(); ++parent) {
785       if (!done[parent] &&
786           archives[parent].mount_path.IsParent(chain.back().source_path)) {
787         done[parent] = true;
788         chain.push_back(archives[parent]);
789         parent = i + 1;  // Search archives[parent]'s parent from the beginning.
790       }
791     }
792
793     // Mount from the tail of chain.
794     for (size_t i = chain.size(); i > 0; --i)
795       DoMountEvent(chromeos::MOUNT_ERROR_NONE, chain[i - 1], kNotRemounting);
796   }
797 }
798
799 void VolumeManager::OnStorageMonitorInitialized() {
800   std::vector<storage_monitor::StorageInfo> storages =
801       storage_monitor::StorageMonitor::GetInstance()->GetAllAvailableStorages();
802   for (size_t i = 0; i < storages.size(); ++i)
803     OnRemovableStorageAttached(storages[i]);
804   storage_monitor::StorageMonitor::GetInstance()->AddObserver(this);
805 }
806
807 void VolumeManager::DoMountEvent(chromeos::MountError error_code,
808                                  const VolumeInfo& volume_info,
809                                  bool is_remounting) {
810   // Archive files are mounted globally in system. We however don't want to show
811   // archives from profile-specific folders (Drive/Downloads) of other users in
812   // multi-profile session. To this end, we filter out archives not on the
813   // volumes already mounted on this VolumeManager instance.
814   if (volume_info.type == VOLUME_TYPE_MOUNTED_ARCHIVE_FILE) {
815     // Source may be in Drive cache folder under the current profile directory.
816     bool from_current_profile =
817         profile_->GetPath().IsParent(volume_info.source_path);
818     for (std::map<std::string, VolumeInfo>::const_iterator iter =
819              mounted_volumes_.begin();
820          !from_current_profile && iter != mounted_volumes_.end();
821          ++iter) {
822       if (iter->second.mount_path.IsParent(volume_info.source_path))
823         from_current_profile = true;
824     }
825     if (!from_current_profile)
826       return;
827   }
828
829   // Filter out removable disks if forbidden by policy for this profile.
830   if (volume_info.type == VOLUME_TYPE_REMOVABLE_DISK_PARTITION &&
831       profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
832     return;
833   }
834
835   if (error_code == chromeos::MOUNT_ERROR_NONE || volume_info.mount_condition) {
836     mounted_volumes_[volume_info.volume_id] = volume_info;
837
838     if (!is_remounting) {
839       UMA_HISTOGRAM_ENUMERATION("FileBrowser.VolumeType",
840                                 volume_info.type,
841                                 NUM_VOLUME_TYPE);
842     }
843   }
844
845   FOR_EACH_OBSERVER(VolumeManagerObserver,
846                     observers_,
847                     OnVolumeMounted(error_code, volume_info, is_remounting));
848 }
849
850 void VolumeManager::DoUnmountEvent(chromeos::MountError error_code,
851                                    const VolumeInfo& volume_info) {
852   if (mounted_volumes_.find(volume_info.volume_id) == mounted_volumes_.end())
853     return;
854   if (error_code == chromeos::MOUNT_ERROR_NONE)
855     mounted_volumes_.erase(volume_info.volume_id);
856
857   FOR_EACH_OBSERVER(VolumeManagerObserver,
858                     observers_,
859                     OnVolumeUnmounted(error_code, volume_info));
860 }
861
862 }  // namespace file_manager