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.
5 // chromeos::StorageMonitorCros implementation.
7 #include "chrome/browser/storage_monitor/storage_monitor_chromeos.h"
9 #include "base/command_line.h"
10 #include "base/files/file_path.h"
11 #include "base/logging.h"
12 #include "base/stl_util.h"
13 #include "base/string_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/utf_string_conversions.h"
16 #include "chrome/browser/storage_monitor/media_storage_util.h"
17 #include "chrome/browser/storage_monitor/media_transfer_protocol_device_observer_linux.h"
18 #include "chrome/browser/storage_monitor/removable_device_constants.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "device/media_transfer_protocol/media_transfer_protocol_manager.h"
27 // Constructs a device name using label or manufacturer (vendor and product)
29 string16
GetDeviceName(const disks::DiskMountManager::Disk
& disk
,
30 string16
* storage_label
,
31 string16
* vendor_name
,
32 string16
* model_name
) {
33 if (disk
.device_type() == DEVICE_TYPE_SD
) {
34 // Mount path of an SD card will be one of the following:
35 // (1) /media/removable/<volume_label>
36 // (2) /media/removable/SD Card
37 // If the volume label is available, mount path will be (1) else (2).
38 base::FilePath
mount_point(disk
.mount_path());
39 const string16
display_name(mount_point
.BaseName().LossyDisplayName());
40 if (!display_name
.empty())
44 const std::string
& device_label
= disk
.device_label();
47 *storage_label
= UTF8ToUTF16(device_label
);
49 *vendor_name
= UTF8ToUTF16(disk
.vendor_name());
51 *model_name
= UTF8ToUTF16(disk
.product_name());
53 if (!device_label
.empty() && IsStringUTF8(device_label
))
54 return UTF8ToUTF16(device_label
);
56 return chrome::MediaStorageUtil::GetFullProductName(disk
.vendor_name(),
60 // Constructs a device id using uuid or manufacturer (vendor and product) id
62 std::string
MakeDeviceUniqueId(const disks::DiskMountManager::Disk
& disk
) {
63 std::string uuid
= disk
.fs_uuid();
65 return chrome::kFSUniqueIdPrefix
+ uuid
;
67 // If one of the vendor or product information is missing, its value in the
69 // Format: VendorModelSerial:VendorInfo:ModelInfo:SerialInfo
70 // TODO(kmadhusu) Extract serial information for the disks and append it to
71 // the device unique id.
72 const std::string
& vendor
= disk
.vendor_id();
73 const std::string
& product
= disk
.product_id();
74 if (vendor
.empty() && product
.empty())
76 return chrome::kVendorModelSerialPrefix
+ vendor
+ ":" + product
+ ":";
79 // Returns true if the requested device is valid, else false. On success, fills
80 // in |unique_id|, |device_label| and |storage_size_in_bytes|.
81 bool GetDeviceInfo(const std::string
& source_path
,
82 std::string
* unique_id
,
83 string16
* device_label
,
84 uint64
* storage_size_in_bytes
,
85 string16
* storage_label
,
86 string16
* vendor_name
,
87 string16
* model_name
) {
88 const disks::DiskMountManager::Disk
* disk
=
89 disks::DiskMountManager::GetInstance()->FindDiskBySourcePath(source_path
);
90 if (!disk
|| disk
->device_type() == DEVICE_TYPE_UNKNOWN
)
94 *unique_id
= MakeDeviceUniqueId(*disk
);
97 *device_label
= GetDeviceName(*disk
, storage_label
,
98 vendor_name
, model_name
);
100 if (storage_size_in_bytes
)
101 *storage_size_in_bytes
= disk
->total_size_in_bytes();
105 // Returns whether the mount point in |mount_info| is a media device or not.
106 bool CheckMountedPathOnFileThread(
107 const disks::DiskMountManager::MountPointInfo
& mount_info
) {
108 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
109 return chrome::MediaStorageUtil::HasDcim(
110 base::FilePath(mount_info
.mount_path
));
115 using content::BrowserThread
;
116 using chrome::StorageInfo
;
118 StorageMonitorCros::StorageMonitorCros()
119 : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
122 StorageMonitorCros::~StorageMonitorCros() {
123 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType
)) {
124 device::MediaTransferProtocolManager::Shutdown();
127 disks::DiskMountManager
* manager
= disks::DiskMountManager::GetInstance();
129 manager
->RemoveObserver(this);
133 void StorageMonitorCros::Init() {
134 DCHECK(disks::DiskMountManager::GetInstance());
135 disks::DiskMountManager::GetInstance()->AddObserver(this);
136 CheckExistingMountPoints();
138 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType
)) {
139 scoped_refptr
<base::MessageLoopProxy
> loop_proxy
;
140 device::MediaTransferProtocolManager::Initialize(loop_proxy
);
142 media_transfer_protocol_device_observer_
.reset(
143 new chrome::MediaTransferProtocolDeviceObserverLinux(receiver()));
147 void StorageMonitorCros::CheckExistingMountPoints() {
148 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
149 const disks::DiskMountManager::MountPointMap
& mount_point_map
=
150 disks::DiskMountManager::GetInstance()->mount_points();
151 for (disks::DiskMountManager::MountPointMap::const_iterator it
=
152 mount_point_map
.begin(); it
!= mount_point_map
.end(); ++it
) {
153 BrowserThread::PostTaskAndReplyWithResult(
154 BrowserThread::FILE, FROM_HERE
,
155 base::Bind(&CheckMountedPathOnFileThread
, it
->second
),
156 base::Bind(&StorageMonitorCros::AddMountedPath
,
157 weak_ptr_factory_
.GetWeakPtr(), it
->second
));
161 void StorageMonitorCros::OnDiskEvent(
162 disks::DiskMountManager::DiskEvent event
,
163 const disks::DiskMountManager::Disk
* disk
) {
166 void StorageMonitorCros::OnDeviceEvent(
167 disks::DiskMountManager::DeviceEvent event
,
168 const std::string
& device_path
) {
171 void StorageMonitorCros::OnMountEvent(
172 disks::DiskMountManager::MountEvent event
,
173 MountError error_code
,
174 const disks::DiskMountManager::MountPointInfo
& mount_info
) {
175 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
177 // Ignore mount points that are not devices.
178 if (mount_info
.mount_type
!= MOUNT_TYPE_DEVICE
)
181 if (error_code
!= MOUNT_ERROR_NONE
)
183 if (mount_info
.mount_condition
!= disks::MOUNT_CONDITION_NONE
)
187 case disks::DiskMountManager::MOUNTING
: {
188 if (ContainsKey(mount_map_
, mount_info
.mount_path
)) {
193 BrowserThread::PostTaskAndReplyWithResult(
194 BrowserThread::FILE, FROM_HERE
,
195 base::Bind(&CheckMountedPathOnFileThread
, mount_info
),
196 base::Bind(&StorageMonitorCros::AddMountedPath
,
197 weak_ptr_factory_
.GetWeakPtr(), mount_info
));
200 case disks::DiskMountManager::UNMOUNTING
: {
201 MountMap::iterator it
= mount_map_
.find(mount_info
.mount_path
);
202 if (it
== mount_map_
.end())
204 receiver()->ProcessDetach(it
->second
.device_id
);
205 mount_map_
.erase(it
);
211 void StorageMonitorCros::OnFormatEvent(
212 disks::DiskMountManager::FormatEvent event
,
213 FormatError error_code
,
214 const std::string
& device_path
) {
217 bool StorageMonitorCros::GetStorageInfoForPath(
218 const base::FilePath
& path
,
219 StorageInfo
* device_info
) const {
220 // TODO(thestig) |media_transfer_protocol_device_observer_| should always be
222 if (media_transfer_protocol_device_observer_
&&
223 media_transfer_protocol_device_observer_
->GetStorageInfoForPath(
224 path
, device_info
)) {
228 if (!path
.IsAbsolute())
231 base::FilePath current
= path
;
232 while (!ContainsKey(mount_map_
, current
.value()) &&
233 current
!= current
.DirName()) {
234 current
= current
.DirName();
237 MountMap::const_iterator info_it
= mount_map_
.find(current
.value());
238 if (info_it
== mount_map_
.end())
242 *device_info
= info_it
->second
;
246 // Callback executed when the unmount call is run by DiskMountManager.
247 // Forwards result to |EjectDevice| caller.
248 void NotifyUnmountResult(
249 base::Callback
<void(chrome::StorageMonitor::EjectStatus
)> callback
,
250 chromeos::MountError error_code
) {
251 if (error_code
== MOUNT_ERROR_NONE
)
252 callback
.Run(chrome::StorageMonitor::EJECT_OK
);
254 callback
.Run(chrome::StorageMonitor::EJECT_FAILURE
);
257 void StorageMonitorCros::EjectDevice(
258 const std::string
& device_id
,
259 base::Callback
<void(EjectStatus
)> callback
) {
260 std::string mount_path
;
261 for (MountMap::const_iterator info_it
= mount_map_
.begin();
262 info_it
!= mount_map_
.end(); ++info_it
) {
263 if (info_it
->second
.device_id
== device_id
)
264 mount_path
= info_it
->first
;
267 if (mount_path
.empty()) {
268 callback
.Run(EJECT_NO_SUCH_DEVICE
);
272 disks::DiskMountManager
* manager
= disks::DiskMountManager::GetInstance();
274 callback
.Run(EJECT_FAILURE
);
278 manager
->UnmountPath(mount_path
, chromeos::UNMOUNT_OPTIONS_NONE
,
279 base::Bind(NotifyUnmountResult
, callback
));
282 void StorageMonitorCros::AddMountedPath(
283 const disks::DiskMountManager::MountPointInfo
& mount_info
, bool has_dcim
) {
284 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
286 if (ContainsKey(mount_map_
, mount_info
.mount_path
)) {
287 // CheckExistingMountPointsOnUIThread() added the mount point information
288 // in the map before the device attached handler is called. Therefore, an
289 // entry for the device already exists in the map.
293 // Get the media device uuid and label if exists.
294 std::string unique_id
;
295 string16 device_label
;
296 string16 storage_label
;
297 string16 vendor_name
;
299 uint64 storage_size_in_bytes
;
300 if (!GetDeviceInfo(mount_info
.source_path
, &unique_id
, &device_label
,
301 &storage_size_in_bytes
, &storage_label
,
302 &vendor_name
, &model_name
))
305 // Keep track of device uuid and label, to see how often we receive empty
307 chrome::MediaStorageUtil::RecordDeviceInfoHistogram(true, unique_id
,
309 if (unique_id
.empty() || device_label
.empty())
312 chrome::MediaStorageUtil::Type type
= has_dcim
?
313 chrome::MediaStorageUtil::REMOVABLE_MASS_STORAGE_WITH_DCIM
:
314 chrome::MediaStorageUtil::REMOVABLE_MASS_STORAGE_NO_DCIM
;
316 std::string device_id
= chrome::MediaStorageUtil::MakeDeviceId(type
,
319 chrome::StorageInfo
object_info(
321 chrome::MediaStorageUtil::GetDisplayNameForDevice(storage_size_in_bytes
,
323 mount_info
.mount_path
,
327 storage_size_in_bytes
);
329 mount_map_
.insert(std::make_pair(mount_info
.mount_path
, object_info
));
331 receiver()->ProcessAttach(object_info
);
334 } // namespace chromeos