1 // Copyright 2014 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 #include "components/storage_monitor/storage_monitor_chromeos.h"
7 #include "base/files/file_path.h"
8 #include "base/logging.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string16.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "components/storage_monitor/media_storage_util.h"
16 #include "components/storage_monitor/media_transfer_protocol_device_observer_linux.h"
17 #include "components/storage_monitor/removable_device_constants.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "device/media_transfer_protocol/media_transfer_protocol_manager.h"
21 using chromeos::disks::DiskMountManager
;
23 namespace storage_monitor
{
27 // Constructs a device id using uuid or manufacturer (vendor and product) id
29 std::string
MakeDeviceUniqueId(const DiskMountManager::Disk
& disk
) {
30 std::string uuid
= disk
.fs_uuid();
32 return kFSUniqueIdPrefix
+ uuid
;
34 // If one of the vendor or product information is missing, its value in the
36 // Format: VendorModelSerial:VendorInfo:ModelInfo:SerialInfo
37 // TODO(kmadhusu) Extract serial information for the disks and append it to
38 // the device unique id.
39 const std::string
& vendor
= disk
.vendor_id();
40 const std::string
& product
= disk
.product_id();
41 if (vendor
.empty() && product
.empty())
43 return kVendorModelSerialPrefix
+ vendor
+ ":" + product
+ ":";
46 // Returns true if the requested device is valid, else false. On success, fills
48 bool GetDeviceInfo(const DiskMountManager::MountPointInfo
& mount_info
,
52 std::string source_path
= mount_info
.source_path
;
54 const DiskMountManager::Disk
* disk
=
55 DiskMountManager::GetInstance()->FindDiskBySourcePath(source_path
);
56 if (!disk
|| disk
->device_type() == chromeos::DEVICE_TYPE_UNKNOWN
)
59 std::string unique_id
= MakeDeviceUniqueId(*disk
);
60 // Keep track of device uuid and label, to see how often we receive empty
62 base::string16 device_label
= base::UTF8ToUTF16(disk
->device_label());
63 MediaStorageUtil::RecordDeviceInfoHistogram(true, unique_id
, device_label
);
64 if (unique_id
.empty())
67 StorageInfo::Type type
= has_dcim
?
68 StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM
:
69 StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM
;
71 *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 DiskMountManager::MountPointInfo
& mount_info
) {
83 DCHECK_CURRENTLY_ON(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 DiskMountManager
* manager
= DiskMountManager::GetInstance();
98 manager
->RemoveObserver(this);
102 void StorageMonitorCros::Init() {
103 DCHECK(DiskMountManager::GetInstance());
104 DiskMountManager::GetInstance()->AddObserver(this);
105 CheckExistingMountPoints();
107 if (!media_transfer_protocol_manager_
) {
108 media_transfer_protocol_manager_
.reset(
109 device::MediaTransferProtocolManager::Initialize(
110 scoped_refptr
<base::SingleThreadTaskRunner
>()));
113 media_transfer_protocol_device_observer_
.reset(
114 new MediaTransferProtocolDeviceObserverLinux(
115 receiver(), media_transfer_protocol_manager_
.get()));
118 void StorageMonitorCros::CheckExistingMountPoints() {
119 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
120 const DiskMountManager::MountPointMap
& mount_point_map
=
121 DiskMountManager::GetInstance()->mount_points();
122 for (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(DiskMountManager::DiskEvent event
,
143 const DiskMountManager::Disk
* disk
) {}
145 void StorageMonitorCros::OnDeviceEvent(DiskMountManager::DeviceEvent event
,
146 const std::string
& device_path
) {}
148 void StorageMonitorCros::OnMountEvent(
149 DiskMountManager::MountEvent event
,
150 chromeos::MountError error_code
,
151 const DiskMountManager::MountPointInfo
& mount_info
) {
152 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
154 // Ignore mount points that are not devices.
155 if (mount_info
.mount_type
!= chromeos::MOUNT_TYPE_DEVICE
)
158 if (error_code
!= chromeos::MOUNT_ERROR_NONE
)
160 if (mount_info
.mount_condition
!= chromeos::disks::MOUNT_CONDITION_NONE
)
164 case DiskMountManager::MOUNTING
: {
165 if (ContainsKey(mount_map_
, mount_info
.mount_path
)) {
170 BrowserThread::PostTaskAndReplyWithResult(
171 BrowserThread::FILE, FROM_HERE
,
172 base::Bind(&CheckMountedPathOnFileThread
, mount_info
),
173 base::Bind(&StorageMonitorCros::AddMountedPath
,
174 weak_ptr_factory_
.GetWeakPtr(), mount_info
));
177 case DiskMountManager::UNMOUNTING
: {
178 MountMap::iterator it
= mount_map_
.find(mount_info
.mount_path
);
179 if (it
== mount_map_
.end())
181 receiver()->ProcessDetach(it
->second
.device_id());
182 mount_map_
.erase(it
);
188 void StorageMonitorCros::OnFormatEvent(DiskMountManager::FormatEvent event
,
189 chromeos::FormatError error_code
,
190 const std::string
& device_path
) {}
192 void StorageMonitorCros::SetMediaTransferProtocolManagerForTest(
193 device::MediaTransferProtocolManager
* test_manager
) {
194 DCHECK(!media_transfer_protocol_manager_
);
195 media_transfer_protocol_manager_
.reset(test_manager
);
199 bool StorageMonitorCros::GetStorageInfoForPath(
200 const base::FilePath
& path
,
201 StorageInfo
* device_info
) const {
204 if (media_transfer_protocol_device_observer_
->GetStorageInfoForPath(
205 path
, device_info
)) {
209 if (!path
.IsAbsolute())
212 base::FilePath current
= path
;
213 while (!ContainsKey(mount_map_
, current
.value()) &&
214 current
!= current
.DirName()) {
215 current
= current
.DirName();
218 MountMap::const_iterator info_it
= mount_map_
.find(current
.value());
219 if (info_it
== mount_map_
.end())
222 *device_info
= info_it
->second
;
226 // Callback executed when the unmount call is run by DiskMountManager.
227 // Forwards result to |EjectDevice| caller.
228 void NotifyUnmountResult(
229 base::Callback
<void(StorageMonitor::EjectStatus
)> callback
,
230 chromeos::MountError error_code
) {
231 if (error_code
== chromeos::MOUNT_ERROR_NONE
)
232 callback
.Run(StorageMonitor::EJECT_OK
);
234 callback
.Run(StorageMonitor::EJECT_FAILURE
);
237 void StorageMonitorCros::EjectDevice(
238 const std::string
& device_id
,
239 base::Callback
<void(EjectStatus
)> callback
) {
240 StorageInfo::Type type
;
241 if (!StorageInfo::CrackDeviceId(device_id
, &type
, NULL
)) {
242 callback
.Run(EJECT_FAILURE
);
246 if (type
== StorageInfo::MTP_OR_PTP
) {
247 media_transfer_protocol_device_observer_
->EjectDevice(device_id
, callback
);
251 std::string mount_path
;
252 for (MountMap::const_iterator info_it
= mount_map_
.begin();
253 info_it
!= mount_map_
.end(); ++info_it
) {
254 if (info_it
->second
.device_id() == device_id
)
255 mount_path
= info_it
->first
;
258 if (mount_path
.empty()) {
259 callback
.Run(EJECT_NO_SUCH_DEVICE
);
263 DiskMountManager
* manager
= DiskMountManager::GetInstance();
265 callback
.Run(EJECT_FAILURE
);
269 manager
->UnmountPath(mount_path
, chromeos::UNMOUNT_OPTIONS_NONE
,
270 base::Bind(NotifyUnmountResult
, callback
));
273 device::MediaTransferProtocolManager
*
274 StorageMonitorCros::media_transfer_protocol_manager() {
275 return media_transfer_protocol_manager_
.get();
278 void StorageMonitorCros::AddMountedPath(
279 const DiskMountManager::MountPointInfo
& mount_info
,
281 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
283 if (ContainsKey(mount_map_
, mount_info
.mount_path
)) {
284 // CheckExistingMountPointsOnUIThread() added the mount point information
285 // in the map before the device attached handler is called. Therefore, an
286 // entry for the device already exists in the map.
290 // Get the media device uuid and label if exists.
292 if (!GetDeviceInfo(mount_info
, has_dcim
, &info
))
295 if (info
.device_id().empty())
298 mount_map_
.insert(std::make_pair(mount_info
.mount_path
, info
));
300 receiver()->ProcessAttach(info
);
303 StorageMonitor
* StorageMonitor::CreateInternal() {
304 return new StorageMonitorCros();
307 } // namespace storage_monitor