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| and |location|.
98 void GetStorageInfo(const std::string
& storage_name
,
99 device::MediaTransferProtocolManager
* mtp_manager
,
101 base::string16
* label
,
102 std::string
* location
) {
103 DCHECK(!storage_name
.empty());
104 const MtpStorageInfo
* storage_info
=
105 mtp_manager
->GetStorageInfo(storage_name
);
110 *id
= GetDeviceIdFromStorageInfo(*storage_info
);
111 *label
= GetDeviceLabelFromStorageInfo(*storage_info
);
112 *location
= GetDeviceLocationFromStorageName(storage_name
);
117 MediaTransferProtocolDeviceObserverLinux::
118 MediaTransferProtocolDeviceObserverLinux(
119 StorageMonitor::Receiver
* receiver
,
120 device::MediaTransferProtocolManager
* mtp_manager
)
121 : mtp_manager_(mtp_manager
),
122 get_storage_info_func_(&GetStorageInfo
),
123 notifications_(receiver
) {
124 mtp_manager_
->AddObserver(this);
128 // This constructor is only used by unit tests.
129 MediaTransferProtocolDeviceObserverLinux::
130 MediaTransferProtocolDeviceObserverLinux(
131 StorageMonitor::Receiver
* receiver
,
132 device::MediaTransferProtocolManager
* mtp_manager
,
133 GetStorageInfoFunc get_storage_info_func
)
134 : mtp_manager_(mtp_manager
),
135 get_storage_info_func_(get_storage_info_func
),
136 notifications_(receiver
) {
139 MediaTransferProtocolDeviceObserverLinux::
140 ~MediaTransferProtocolDeviceObserverLinux() {
141 mtp_manager_
->RemoveObserver(this);
144 bool MediaTransferProtocolDeviceObserverLinux::GetStorageInfoForPath(
145 const base::FilePath
& path
,
146 StorageInfo
* storage_info
) const {
147 DCHECK(storage_info
);
149 if (!path
.IsAbsolute())
152 std::vector
<base::FilePath::StringType
> path_components
;
153 path
.GetComponents(&path_components
);
154 if (path_components
.size() < 2)
157 // First and second component of the path specifies the device location.
158 // E.g.: If |path| is "/usb:2,2:65537/DCIM/Folder_a", "/usb:2,2:65537" is the
160 StorageLocationToInfoMap::const_iterator info_it
=
161 storage_map_
.find(GetDeviceLocationFromStorageName(path_components
[1]));
162 if (info_it
== storage_map_
.end())
165 *storage_info
= info_it
->second
;
169 void MediaTransferProtocolDeviceObserverLinux::EjectDevice(
170 const std::string
& device_id
,
171 base::Callback
<void(StorageMonitor::EjectStatus
)> callback
) {
172 std::string location
;
173 if (!GetLocationForDeviceId(device_id
, &location
)) {
174 callback
.Run(StorageMonitor::EJECT_NO_SUCH_DEVICE
);
178 // TODO(thestig): Change this to tell the mtp manager to eject the device.
180 StorageChanged(false, location
);
181 callback
.Run(StorageMonitor::EJECT_OK
);
184 // device::MediaTransferProtocolManager::Observer override.
185 void MediaTransferProtocolDeviceObserverLinux::StorageChanged(
187 const std::string
& storage_name
) {
188 DCHECK(!storage_name
.empty());
190 // New storage is attached.
192 std::string device_id
;
193 base::string16 storage_label
;
194 std::string location
;
195 get_storage_info_func_(storage_name
, mtp_manager_
,
196 &device_id
, &storage_label
, &location
);
198 // Keep track of device id and device name to see how often we receive
200 MediaStorageUtil::RecordDeviceInfoHistogram(false, device_id
,
202 if (device_id
.empty() || storage_label
.empty())
205 DCHECK(!ContainsKey(storage_map_
, location
));
207 StorageInfo
storage_info(device_id
, location
, storage_label
,
208 base::string16(), base::string16(), 0);
209 storage_map_
[location
] = storage_info
;
210 notifications_
->ProcessAttach(storage_info
);
212 // Existing storage is detached.
213 StorageLocationToInfoMap::iterator it
=
214 storage_map_
.find(GetDeviceLocationFromStorageName(storage_name
));
215 if (it
== storage_map_
.end())
217 notifications_
->ProcessDetach(it
->second
.device_id());
218 storage_map_
.erase(it
);
222 void MediaTransferProtocolDeviceObserverLinux::EnumerateStorages() {
223 typedef std::vector
<std::string
> StorageList
;
224 StorageList storages
= mtp_manager_
->GetStorages();
225 for (StorageList::const_iterator storage_iter
= storages
.begin();
226 storage_iter
!= storages
.end(); ++storage_iter
) {
227 StorageChanged(true, *storage_iter
);
231 bool MediaTransferProtocolDeviceObserverLinux::GetLocationForDeviceId(
232 const std::string
& device_id
, std::string
* location
) const {
233 for (StorageLocationToInfoMap::const_iterator it
= storage_map_
.begin();
234 it
!= storage_map_
.end(); ++it
) {
235 if (it
->second
.device_id() == device_id
) {
236 *location
= it
->first
;
244 } // namespace storage_monitor