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/media_transfer_protocol_device_observer_linux.h"
9 #include "base/files/file_path.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "components/storage_monitor/media_storage_util.h"
15 #include "components/storage_monitor/removable_device_constants.h"
16 #include "device/media_transfer_protocol/mtp_storage_info.pb.h"
18 namespace storage_monitor
{
22 // Device root path constant.
23 const char kRootPath
[] = "/";
25 // Constructs and returns the location of the device using the |storage_name|.
26 std::string
GetDeviceLocationFromStorageName(const std::string
& storage_name
) {
27 // Construct a dummy device path using the storage name. This is only used
28 // for registering device media file system.
29 // E.g.: If the |storage_name| is "usb:2,2:12345" then "/usb:2,2:12345" is the
31 DCHECK(!storage_name
.empty());
32 return kRootPath
+ storage_name
;
35 // Returns the storage identifier of the device from the given |storage_name|.
36 // E.g. If the |storage_name| is "usb:2,2:65537", the storage identifier is
38 std::string
GetStorageIdFromStorageName(const std::string
& storage_name
) {
39 std::vector
<std::string
> name_parts
;
40 base::SplitString(storage_name
, ':', &name_parts
);
41 return name_parts
.size() == 3 ? name_parts
[2] : std::string();
44 // Returns a unique device id from the given |storage_info|.
45 std::string
GetDeviceIdFromStorageInfo(const MtpStorageInfo
& storage_info
) {
46 const std::string storage_id
=
47 GetStorageIdFromStorageName(storage_info
.storage_name());
48 if (storage_id
.empty())
51 // Some devices have multiple data stores. Therefore, include storage id as
52 // part of unique id along with vendor, model and volume information.
53 const std::string vendor_id
= base::UintToString(storage_info
.vendor_id());
54 const std::string model_id
= base::UintToString(storage_info
.product_id());
55 return StorageInfo::MakeDeviceId(
56 StorageInfo::MTP_OR_PTP
,
57 kVendorModelVolumeStoragePrefix
+ vendor_id
+ ":" + model_id
+ ":" +
58 storage_info
.volume_identifier() + ":" + storage_id
);
61 // Returns the |data_store_id| string in the required format.
62 // If the |data_store_id| is 65537, this function returns " (65537)".
63 std::string
GetFormattedIdString(const std::string
& data_store_id
) {
64 return ("(" + data_store_id
+ ")");
67 // Helper function to get device label from storage information.
68 base::string16
GetDeviceLabelFromStorageInfo(
69 const MtpStorageInfo
& storage_info
) {
70 std::string device_label
;
71 const std::string
& vendor_name
= storage_info
.vendor();
72 device_label
= vendor_name
;
74 const std::string
& product_name
= storage_info
.product();
75 if (!product_name
.empty()) {
76 if (!device_label
.empty())
78 device_label
+= product_name
;
81 // Add the data store id to the device label.
82 if (!device_label
.empty()) {
83 const std::string
& volume_id
= storage_info
.volume_identifier();
84 if (!volume_id
.empty()) {
85 device_label
+= GetFormattedIdString(volume_id
);
87 const std::string data_store_id
=
88 GetStorageIdFromStorageName(storage_info
.storage_name());
89 if (!data_store_id
.empty())
90 device_label
+= GetFormattedIdString(data_store_id
);
93 return base::UTF8ToUTF16(device_label
);
96 // Helper function to get the device storage details such as device id, label
97 // and location. On success and fills in |id|, |label|, |location|,
98 // |vendor_name|, and |product_name|.
99 void GetStorageInfo(const std::string
& storage_name
,
100 device::MediaTransferProtocolManager
* mtp_manager
,
102 base::string16
* label
,
103 std::string
* location
,
104 base::string16
* vendor_name
,
105 base::string16
* product_name
) {
106 DCHECK(!storage_name
.empty());
107 const MtpStorageInfo
* storage_info
=
108 mtp_manager
->GetStorageInfo(storage_name
);
113 *id
= GetDeviceIdFromStorageInfo(*storage_info
);
114 *label
= GetDeviceLabelFromStorageInfo(*storage_info
);
115 *location
= GetDeviceLocationFromStorageName(storage_name
);
116 *vendor_name
= base::UTF8ToUTF16(storage_info
->vendor());
117 *product_name
= base::UTF8ToUTF16(storage_info
->product());
122 MediaTransferProtocolDeviceObserverLinux::
123 MediaTransferProtocolDeviceObserverLinux(
124 StorageMonitor::Receiver
* receiver
,
125 device::MediaTransferProtocolManager
* mtp_manager
)
126 : mtp_manager_(mtp_manager
),
127 get_storage_info_func_(&GetStorageInfo
),
128 notifications_(receiver
) {
129 mtp_manager_
->AddObserver(this);
133 // This constructor is only used by unit tests.
134 MediaTransferProtocolDeviceObserverLinux::
135 MediaTransferProtocolDeviceObserverLinux(
136 StorageMonitor::Receiver
* receiver
,
137 device::MediaTransferProtocolManager
* mtp_manager
,
138 GetStorageInfoFunc get_storage_info_func
)
139 : mtp_manager_(mtp_manager
),
140 get_storage_info_func_(get_storage_info_func
),
141 notifications_(receiver
) {
144 MediaTransferProtocolDeviceObserverLinux::
145 ~MediaTransferProtocolDeviceObserverLinux() {
146 mtp_manager_
->RemoveObserver(this);
149 bool MediaTransferProtocolDeviceObserverLinux::GetStorageInfoForPath(
150 const base::FilePath
& path
,
151 StorageInfo
* storage_info
) const {
152 DCHECK(storage_info
);
154 if (!path
.IsAbsolute())
157 std::vector
<base::FilePath::StringType
> path_components
;
158 path
.GetComponents(&path_components
);
159 if (path_components
.size() < 2)
162 // First and second component of the path specifies the device location.
163 // E.g.: If |path| is "/usb:2,2:65537/DCIM/Folder_a", "/usb:2,2:65537" is the
165 StorageLocationToInfoMap::const_iterator info_it
=
166 storage_map_
.find(GetDeviceLocationFromStorageName(path_components
[1]));
167 if (info_it
== storage_map_
.end())
170 *storage_info
= info_it
->second
;
174 void MediaTransferProtocolDeviceObserverLinux::EjectDevice(
175 const std::string
& device_id
,
176 base::Callback
<void(StorageMonitor::EjectStatus
)> callback
) {
177 std::string location
;
178 if (!GetLocationForDeviceId(device_id
, &location
)) {
179 callback
.Run(StorageMonitor::EJECT_NO_SUCH_DEVICE
);
183 // TODO(thestig): Change this to tell the mtp manager to eject the device.
185 StorageChanged(false, location
);
186 callback
.Run(StorageMonitor::EJECT_OK
);
189 // device::MediaTransferProtocolManager::Observer override.
190 void MediaTransferProtocolDeviceObserverLinux::StorageChanged(
192 const std::string
& storage_name
) {
193 DCHECK(!storage_name
.empty());
195 // New storage is attached.
197 std::string device_id
;
198 base::string16 storage_label
;
199 std::string location
;
200 base::string16 vendor_name
;
201 base::string16 product_name
;
202 get_storage_info_func_(storage_name
, mtp_manager_
,
203 &device_id
, &storage_label
, &location
,
204 &vendor_name
, &product_name
);
206 // Keep track of device id and device name to see how often we receive
208 MediaStorageUtil::RecordDeviceInfoHistogram(false, device_id
,
210 if (device_id
.empty() || storage_label
.empty())
213 DCHECK(!ContainsKey(storage_map_
, location
));
215 StorageInfo
storage_info(device_id
, location
, storage_label
,
216 vendor_name
, product_name
, 0);
217 storage_map_
[location
] = storage_info
;
218 notifications_
->ProcessAttach(storage_info
);
220 // Existing storage is detached.
221 StorageLocationToInfoMap::iterator it
=
222 storage_map_
.find(GetDeviceLocationFromStorageName(storage_name
));
223 if (it
== storage_map_
.end())
225 notifications_
->ProcessDetach(it
->second
.device_id());
226 storage_map_
.erase(it
);
230 void MediaTransferProtocolDeviceObserverLinux::EnumerateStorages() {
231 typedef std::vector
<std::string
> StorageList
;
232 StorageList storages
= mtp_manager_
->GetStorages();
233 for (StorageList::const_iterator storage_iter
= storages
.begin();
234 storage_iter
!= storages
.end(); ++storage_iter
) {
235 StorageChanged(true, *storage_iter
);
239 bool MediaTransferProtocolDeviceObserverLinux::GetLocationForDeviceId(
240 const std::string
& device_id
, std::string
* location
) const {
241 for (StorageLocationToInfoMap::const_iterator it
= storage_map_
.begin();
242 it
!= storage_map_
.end(); ++it
) {
243 if (it
->second
.device_id() == device_id
) {
244 *location
= it
->first
;
252 } // namespace storage_monitor