• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "components/storage_monitor/media_transfer_protocol_device_observer_linux.h"
6 
7 #include <vector>
8 
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"
17 
18 namespace storage_monitor {
19 
20 namespace {
21 
22 // Device root path constant.
23 const char kRootPath[] = "/";
24 
25 // Constructs and returns the location of the device using the |storage_name|.
GetDeviceLocationFromStorageName(const std::string & 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
30   // device location.
31   DCHECK(!storage_name.empty());
32   return kRootPath + storage_name;
33 }
34 
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
37 // "65537".
GetStorageIdFromStorageName(const std::string & storage_name)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();
42 }
43 
44 // Returns a unique device id from the given |storage_info|.
GetDeviceIdFromStorageInfo(const MtpStorageInfo & 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())
49     return std::string();
50 
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);
59 }
60 
61 // Returns the |data_store_id| string in the required format.
62 // If the |data_store_id| is 65537, this function returns " (65537)".
GetFormattedIdString(const std::string & data_store_id)63 std::string GetFormattedIdString(const std::string& data_store_id) {
64   return ("(" + data_store_id + ")");
65 }
66 
67 // Helper function to get device label from storage information.
GetDeviceLabelFromStorageInfo(const MtpStorageInfo & storage_info)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;
73 
74   const std::string& product_name = storage_info.product();
75   if (!product_name.empty()) {
76     if (!device_label.empty())
77       device_label += " ";
78     device_label += product_name;
79   }
80 
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);
86     } else {
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);
91     }
92   }
93   return base::UTF8ToUTF16(device_label);
94 }
95 
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|.
GetStorageInfo(const std::string & storage_name,device::MediaTransferProtocolManager * mtp_manager,std::string * id,base::string16 * label,std::string * location,base::string16 * vendor_name,base::string16 * product_name)99 void GetStorageInfo(const std::string& storage_name,
100                     device::MediaTransferProtocolManager* mtp_manager,
101                     std::string* id,
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);
109 
110   if (!storage_info)
111     return;
112 
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());
118 }
119 
120 }  // namespace
121 
122 MediaTransferProtocolDeviceObserverLinux::
MediaTransferProtocolDeviceObserverLinux(StorageMonitor::Receiver * receiver,device::MediaTransferProtocolManager * mtp_manager)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);
130   EnumerateStorages();
131 }
132 
133 // This constructor is only used by unit tests.
134 MediaTransferProtocolDeviceObserverLinux::
MediaTransferProtocolDeviceObserverLinux(StorageMonitor::Receiver * receiver,device::MediaTransferProtocolManager * mtp_manager,GetStorageInfoFunc get_storage_info_func)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) {
142 }
143 
144 MediaTransferProtocolDeviceObserverLinux::
~MediaTransferProtocolDeviceObserverLinux()145 ~MediaTransferProtocolDeviceObserverLinux() {
146   mtp_manager_->RemoveObserver(this);
147 }
148 
GetStorageInfoForPath(const base::FilePath & path,StorageInfo * storage_info) const149 bool MediaTransferProtocolDeviceObserverLinux::GetStorageInfoForPath(
150     const base::FilePath& path,
151     StorageInfo* storage_info) const {
152   DCHECK(storage_info);
153 
154   if (!path.IsAbsolute())
155     return false;
156 
157   std::vector<base::FilePath::StringType> path_components;
158   path.GetComponents(&path_components);
159   if (path_components.size() < 2)
160     return false;
161 
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
164   // device location.
165   StorageLocationToInfoMap::const_iterator info_it =
166       storage_map_.find(GetDeviceLocationFromStorageName(path_components[1]));
167   if (info_it == storage_map_.end())
168     return false;
169 
170   *storage_info = info_it->second;
171   return true;
172 }
173 
EjectDevice(const std::string & device_id,base::Callback<void (StorageMonitor::EjectStatus)> callback)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);
180     return;
181   }
182 
183   // TODO(thestig): Change this to tell the mtp manager to eject the device.
184 
185   StorageChanged(false, location);
186   callback.Run(StorageMonitor::EJECT_OK);
187 }
188 
189 // device::MediaTransferProtocolManager::Observer override.
StorageChanged(bool is_attached,const std::string & storage_name)190 void MediaTransferProtocolDeviceObserverLinux::StorageChanged(
191     bool is_attached,
192     const std::string& storage_name) {
193   DCHECK(!storage_name.empty());
194 
195   // New storage is attached.
196   if (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);
205 
206     // Keep track of device id and device name to see how often we receive
207     // empty values.
208     MediaStorageUtil::RecordDeviceInfoHistogram(false, device_id,
209                                                 storage_label);
210     if (device_id.empty() || storage_label.empty())
211       return;
212 
213     DCHECK(!ContainsKey(storage_map_, location));
214 
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);
219   } else {
220     // Existing storage is detached.
221     StorageLocationToInfoMap::iterator it =
222         storage_map_.find(GetDeviceLocationFromStorageName(storage_name));
223     if (it == storage_map_.end())
224       return;
225     notifications_->ProcessDetach(it->second.device_id());
226     storage_map_.erase(it);
227   }
228 }
229 
EnumerateStorages()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);
236   }
237 }
238 
GetLocationForDeviceId(const std::string & device_id,std::string * location) const239 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;
245       return true;
246     }
247   }
248 
249   return false;
250 }
251 
252 }  // namespace storage_monitor
253