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