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|. 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". 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|. 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)". 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. 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|. 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:: 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:: 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:: 145 ~MediaTransferProtocolDeviceObserverLinux() { 146 mtp_manager_->RemoveObserver(this); 147 } 148 149 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 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. 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 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 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; 245 return true; 246 } 247 } 248 249 return false; 250 } 251 252 } // namespace storage_monitor 253