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/files/file_path.h"
10 #include "base/logging.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/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 "content/public/browser/browser_thread.h"
20 #include "device/media_transfer_protocol/media_transfer_protocol_manager.h"
26 // Constructs a device id using uuid or manufacturer (vendor and product) id
28 std::string
MakeDeviceUniqueId(const disks::DiskMountManager::Disk
& disk
) {
29 std::string uuid
= disk
.fs_uuid();
31 return kFSUniqueIdPrefix
+ uuid
;
33 // If one of the vendor or product information is missing, its value in the
35 // Format: VendorModelSerial:VendorInfo:ModelInfo:SerialInfo
36 // TODO(kmadhusu) Extract serial information for the disks and append it to
37 // the device unique id.
38 const std::string
& vendor
= disk
.vendor_id();
39 const std::string
& product
= disk
.product_id();
40 if (vendor
.empty() && product
.empty())
42 return kVendorModelSerialPrefix
+ vendor
+ ":" + product
+ ":";
45 // Returns true if the requested device is valid, else false. On success, fills
47 bool GetDeviceInfo(const disks::DiskMountManager::MountPointInfo
& mount_info
,
51 std::string source_path
= mount_info
.source_path
;
53 const disks::DiskMountManager::Disk
* disk
=
54 disks::DiskMountManager::GetInstance()->FindDiskBySourcePath(source_path
);
55 if (!disk
|| disk
->device_type() == DEVICE_TYPE_UNKNOWN
)
58 std::string unique_id
= MakeDeviceUniqueId(*disk
);
59 // Keep track of device uuid and label, to see how often we receive empty
61 base::string16 device_label
= base::UTF8ToUTF16(disk
->device_label());
62 MediaStorageUtil::RecordDeviceInfoHistogram(true, unique_id
, device_label
);
63 if (unique_id
.empty())
66 StorageInfo::Type type
= has_dcim
?
67 StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM
:
68 StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM
;
70 *info
= StorageInfo(StorageInfo::MakeDeviceId(type
, unique_id
),
72 mount_info
.mount_path
,
74 base::UTF8ToUTF16(disk
->vendor_name()),
75 base::UTF8ToUTF16(disk
->product_name()),
76 disk
->total_size_in_bytes());
80 // Returns whether the mount point in |mount_info| is a media device or not.
81 bool CheckMountedPathOnFileThread(
82 const disks::DiskMountManager::MountPointInfo
& mount_info
) {
83 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
84 return MediaStorageUtil::HasDcim(base::FilePath(mount_info
.mount_path
));
89 using content::BrowserThread
;
91 StorageMonitorCros::StorageMonitorCros()
92 : weak_ptr_factory_(this) {
95 StorageMonitorCros::~StorageMonitorCros() {
96 disks::DiskMountManager
* manager
= disks::DiskMountManager::GetInstance();
98 manager
->RemoveObserver(this);
102 void StorageMonitorCros::Init() {
103 DCHECK(disks::DiskMountManager::GetInstance());
104 disks::DiskMountManager::GetInstance()->AddObserver(this);
105 CheckExistingMountPoints();
107 if (!media_transfer_protocol_manager_
) {
108 scoped_refptr
<base::MessageLoopProxy
> loop_proxy
;
109 media_transfer_protocol_manager_
.reset(
110 device::MediaTransferProtocolManager::Initialize(loop_proxy
));
113 media_transfer_protocol_device_observer_
.reset(
114 new MediaTransferProtocolDeviceObserverLinux(
115 receiver(), media_transfer_protocol_manager_
.get()));
118 void StorageMonitorCros::CheckExistingMountPoints() {
119 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
120 const disks::DiskMountManager::MountPointMap
& mount_point_map
=
121 disks::DiskMountManager::GetInstance()->mount_points();
122 for (disks::DiskMountManager::MountPointMap::const_iterator it
=
123 mount_point_map
.begin(); it
!= mount_point_map
.end(); ++it
) {
124 BrowserThread::PostTaskAndReplyWithResult(
125 BrowserThread::FILE, FROM_HERE
,
126 base::Bind(&CheckMountedPathOnFileThread
, it
->second
),
127 base::Bind(&StorageMonitorCros::AddMountedPath
,
128 weak_ptr_factory_
.GetWeakPtr(), it
->second
));
131 // Note: relies on scheduled tasks on the file thread being sequential. This
132 // block needs to follow the for loop, so that the DoNothing call on the FILE
133 // thread happens after the scheduled metadata retrievals, meaning that the
134 // reply callback will then happen after all the AddNewMount calls.
135 BrowserThread::PostTaskAndReply(
136 BrowserThread::FILE, FROM_HERE
,
137 base::Bind(&base::DoNothing
),
138 base::Bind(&StorageMonitorCros::MarkInitialized
,
139 weak_ptr_factory_
.GetWeakPtr()));
142 void StorageMonitorCros::OnDiskEvent(
143 disks::DiskMountManager::DiskEvent event
,
144 const disks::DiskMountManager::Disk
* disk
) {
147 void StorageMonitorCros::OnDeviceEvent(
148 disks::DiskMountManager::DeviceEvent event
,
149 const std::string
& device_path
) {
152 void StorageMonitorCros::OnMountEvent(
153 disks::DiskMountManager::MountEvent event
,
154 MountError error_code
,
155 const disks::DiskMountManager::MountPointInfo
& mount_info
) {
156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
158 // Ignore mount points that are not devices.
159 if (mount_info
.mount_type
!= MOUNT_TYPE_DEVICE
)
162 if (error_code
!= MOUNT_ERROR_NONE
)
164 if (mount_info
.mount_condition
!= disks::MOUNT_CONDITION_NONE
)
168 case disks::DiskMountManager::MOUNTING
: {
169 if (ContainsKey(mount_map_
, mount_info
.mount_path
)) {
174 BrowserThread::PostTaskAndReplyWithResult(
175 BrowserThread::FILE, FROM_HERE
,
176 base::Bind(&CheckMountedPathOnFileThread
, mount_info
),
177 base::Bind(&StorageMonitorCros::AddMountedPath
,
178 weak_ptr_factory_
.GetWeakPtr(), mount_info
));
181 case disks::DiskMountManager::UNMOUNTING
: {
182 MountMap::iterator it
= mount_map_
.find(mount_info
.mount_path
);
183 if (it
== mount_map_
.end())
185 receiver()->ProcessDetach(it
->second
.device_id());
186 mount_map_
.erase(it
);
192 void StorageMonitorCros::OnFormatEvent(
193 disks::DiskMountManager::FormatEvent event
,
194 FormatError error_code
,
195 const std::string
& device_path
) {
198 void StorageMonitorCros::SetMediaTransferProtocolManagerForTest(
199 device::MediaTransferProtocolManager
* test_manager
) {
200 DCHECK(!media_transfer_protocol_manager_
);
201 media_transfer_protocol_manager_
.reset(test_manager
);
205 bool StorageMonitorCros::GetStorageInfoForPath(
206 const base::FilePath
& path
,
207 StorageInfo
* device_info
) const {
210 if (media_transfer_protocol_device_observer_
->GetStorageInfoForPath(
211 path
, device_info
)) {
215 if (!path
.IsAbsolute())
218 base::FilePath current
= path
;
219 while (!ContainsKey(mount_map_
, current
.value()) &&
220 current
!= current
.DirName()) {
221 current
= current
.DirName();
224 MountMap::const_iterator info_it
= mount_map_
.find(current
.value());
225 if (info_it
== mount_map_
.end())
228 *device_info
= info_it
->second
;
232 // Callback executed when the unmount call is run by DiskMountManager.
233 // Forwards result to |EjectDevice| caller.
234 void NotifyUnmountResult(
235 base::Callback
<void(StorageMonitor::EjectStatus
)> callback
,
236 chromeos::MountError error_code
) {
237 if (error_code
== MOUNT_ERROR_NONE
)
238 callback
.Run(StorageMonitor::EJECT_OK
);
240 callback
.Run(StorageMonitor::EJECT_FAILURE
);
243 void StorageMonitorCros::EjectDevice(
244 const std::string
& device_id
,
245 base::Callback
<void(EjectStatus
)> callback
) {
246 StorageInfo::Type type
;
247 if (!StorageInfo::CrackDeviceId(device_id
, &type
, NULL
)) {
248 callback
.Run(EJECT_FAILURE
);
252 if (type
== StorageInfo::MTP_OR_PTP
) {
253 media_transfer_protocol_device_observer_
->EjectDevice(device_id
, callback
);
257 std::string mount_path
;
258 for (MountMap::const_iterator info_it
= mount_map_
.begin();
259 info_it
!= mount_map_
.end(); ++info_it
) {
260 if (info_it
->second
.device_id() == device_id
)
261 mount_path
= info_it
->first
;
264 if (mount_path
.empty()) {
265 callback
.Run(EJECT_NO_SUCH_DEVICE
);
269 disks::DiskMountManager
* manager
= disks::DiskMountManager::GetInstance();
271 callback
.Run(EJECT_FAILURE
);
275 manager
->UnmountPath(mount_path
, chromeos::UNMOUNT_OPTIONS_NONE
,
276 base::Bind(NotifyUnmountResult
, callback
));
279 device::MediaTransferProtocolManager
*
280 StorageMonitorCros::media_transfer_protocol_manager() {
281 return media_transfer_protocol_manager_
.get();
284 void StorageMonitorCros::AddMountedPath(
285 const disks::DiskMountManager::MountPointInfo
& mount_info
, bool has_dcim
) {
286 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
288 if (ContainsKey(mount_map_
, mount_info
.mount_path
)) {
289 // CheckExistingMountPointsOnUIThread() added the mount point information
290 // in the map before the device attached handler is called. Therefore, an
291 // entry for the device already exists in the map.
295 // Get the media device uuid and label if exists.
297 if (!GetDeviceInfo(mount_info
, has_dcim
, &info
))
300 if (info
.device_id().empty())
303 mount_map_
.insert(std::make_pair(mount_info
.mount_path
, info
));
305 receiver()->ProcessAttach(info
);
308 } // namespace chromeos
310 StorageMonitor
* StorageMonitor::Create() {
311 return new chromeos::StorageMonitorCros();