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 #include "chrome/browser/storage_monitor/media_storage_util.h"
9 #include "base/callback.h"
10 #include "base/file_util.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/storage_monitor/removable_device_constants.h"
16 #include "chrome/browser/storage_monitor/storage_monitor.h"
17 #include "content/public/browser/browser_thread.h"
19 using content::BrowserThread
;
23 // MediaDeviceNotification.DeviceInfo histogram values.
24 enum DeviceInfoHistogramBuckets
{
25 MASS_STORAGE_DEVICE_NAME_AND_UUID_AVAILABLE
,
26 MASS_STORAGE_DEVICE_UUID_MISSING
,
27 MASS_STORAGE_DEVICE_NAME_MISSING
,
28 MASS_STORAGE_DEVICE_NAME_AND_UUID_MISSING
,
29 MTP_STORAGE_DEVICE_NAME_AND_UUID_AVAILABLE
,
30 MTP_STORAGE_DEVICE_UUID_MISSING
,
31 MTP_STORAGE_DEVICE_NAME_MISSING
,
32 MTP_STORAGE_DEVICE_NAME_AND_UUID_MISSING
,
33 DEVICE_INFO_BUCKET_BOUNDARY
37 const char kRootPath
[] = "/";
40 typedef std::vector
<StorageInfo
> StorageInfoList
;
42 base::FilePath::StringType
FindRemovableStorageLocationById(
43 const std::string
& device_id
) {
44 StorageInfoList devices
=
45 StorageMonitor::GetInstance()->GetAllAvailableStorages();
46 for (StorageInfoList::const_iterator it
= devices
.begin();
47 it
!= devices
.end(); ++it
) {
48 if (it
->device_id() == device_id
49 && StorageInfo::IsRemovableDevice(device_id
))
50 return it
->location();
52 return base::FilePath::StringType();
55 void FilterAttachedDevicesOnFileThread(MediaStorageUtil::DeviceIdSet
* devices
) {
56 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
57 MediaStorageUtil::DeviceIdSet missing_devices
;
59 for (MediaStorageUtil::DeviceIdSet::const_iterator it
= devices
->begin();
62 StorageInfo::Type type
;
63 std::string unique_id
;
64 if (!StorageInfo::CrackDeviceId(*it
, &type
, &unique_id
)) {
65 missing_devices
.insert(*it
);
69 if (type
== StorageInfo::FIXED_MASS_STORAGE
||
70 type
== StorageInfo::ITUNES
||
71 type
== StorageInfo::IPHOTO
||
72 type
== StorageInfo::PICASA
) {
73 if (!base::PathExists(base::FilePath::FromUTF8Unsafe(unique_id
)))
74 missing_devices
.insert(*it
);
78 if (!MediaStorageUtil::IsRemovableStorageAttached(*it
))
79 missing_devices
.insert(*it
);
82 for (MediaStorageUtil::DeviceIdSet::const_iterator it
=
83 missing_devices
.begin();
84 it
!= missing_devices
.end();
93 bool MediaStorageUtil::HasDcim(const base::FilePath
& mount_point
) {
94 DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
96 base::FilePath::StringType
dcim_dir(kDCIMDirectoryName
);
97 if (!base::DirectoryExists(mount_point
.Append(dcim_dir
))) {
98 // Check for lowercase 'dcim' as well.
99 base::FilePath
dcim_path_lower(
100 mount_point
.Append(StringToLowerASCII(dcim_dir
)));
101 if (!base::DirectoryExists(dcim_path_lower
))
108 bool MediaStorageUtil::CanCreateFileSystem(const std::string
& device_id
,
109 const base::FilePath
& path
) {
110 StorageInfo::Type type
;
111 if (!StorageInfo::CrackDeviceId(device_id
, &type
, NULL
))
114 if (type
== StorageInfo::MAC_IMAGE_CAPTURE
)
117 return path
.IsAbsolute() && !path
.ReferencesParent();
121 void MediaStorageUtil::FilterAttachedDevices(DeviceIdSet
* devices
,
122 const base::Closure
& done
) {
123 if (BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
124 FilterAttachedDevicesOnFileThread(devices
);
128 BrowserThread::PostTaskAndReply(BrowserThread::FILE,
130 base::Bind(&FilterAttachedDevicesOnFileThread
,
135 // TODO(kmadhusu) Write unit tests for GetDeviceInfoFromPath().
136 bool MediaStorageUtil::GetDeviceInfoFromPath(const base::FilePath
& path
,
137 StorageInfo
* device_info
,
138 base::FilePath
* relative_path
) {
140 DCHECK(relative_path
);
142 if (!path
.IsAbsolute())
146 StorageMonitor
* monitor
= StorageMonitor::GetInstance();
147 bool found_device
= monitor
->GetStorageInfoForPath(path
, &info
);
149 if (found_device
&& StorageInfo::IsRemovableDevice(info
.device_id())) {
150 base::FilePath sub_folder_path
;
151 base::FilePath
device_path(info
.location());
152 if (path
!= device_path
) {
153 bool success
= device_path
.AppendRelativePath(path
, &sub_folder_path
);
158 *relative_path
= sub_folder_path
;
162 // On Posix systems, there's one root so any absolute path could be valid.
163 // TODO(gbillock): Delete this stanza? Posix systems should have the root
164 // volume information. If not, we should move the below into the
165 // right GetStorageInfoForPath implementations.
166 #if !defined(OS_POSIX)
171 // Handle non-removable devices. Note: this is just overwriting
172 // good values from StorageMonitor.
173 // TODO(gbillock): Make sure return values from that class are definitive,
174 // and don't do this here.
176 StorageInfo::MakeDeviceId(StorageInfo::FIXED_MASS_STORAGE
,
177 path
.AsUTF8Unsafe()));
179 *relative_path
= base::FilePath();
184 base::FilePath
MediaStorageUtil::FindDevicePathById(
185 const std::string
& device_id
) {
186 StorageInfo::Type type
;
187 std::string unique_id
;
188 if (!StorageInfo::CrackDeviceId(device_id
, &type
, &unique_id
))
189 return base::FilePath();
191 if (type
== StorageInfo::FIXED_MASS_STORAGE
||
192 type
== StorageInfo::ITUNES
||
193 type
== StorageInfo::IPHOTO
||
194 type
== StorageInfo::PICASA
) {
195 // For this type, the unique_id is the path.
196 return base::FilePath::FromUTF8Unsafe(unique_id
);
199 // For ImageCapture, the synthetic filesystem will be rooted at a fake
200 // top-level directory which is the device_id.
201 if (type
== StorageInfo::MAC_IMAGE_CAPTURE
) {
203 return base::FilePath(kRootPath
+ device_id
);
207 DCHECK(type
== StorageInfo::MTP_OR_PTP
||
208 type
== StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM
||
209 type
== StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM
);
210 return base::FilePath(FindRemovableStorageLocationById(device_id
));
214 void MediaStorageUtil::RecordDeviceInfoHistogram(
216 const std::string
& device_uuid
,
217 const base::string16
& device_label
) {
218 unsigned int event_number
= 0;
222 if (device_label
.empty())
225 if (device_uuid
.empty())
227 enum DeviceInfoHistogramBuckets event
=
228 static_cast<enum DeviceInfoHistogramBuckets
>(event_number
);
229 if (event
>= DEVICE_INFO_BUCKET_BOUNDARY
) {
233 UMA_HISTOGRAM_ENUMERATION("MediaDeviceNotifications.DeviceInfo", event
,
234 DEVICE_INFO_BUCKET_BOUNDARY
);
237 bool MediaStorageUtil::IsRemovableStorageAttached(const std::string
& id
) {
238 StorageInfoList devices
=
239 StorageMonitor::GetInstance()->GetAllAvailableStorages();
240 for (StorageInfoList::const_iterator it
= devices
.begin();
241 it
!= devices
.end(); ++it
) {
242 if (StorageInfo::IsRemovableDevice(id
) && it
->device_id() == id
)