media_transfer_protocol_device_observer_linux.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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 18namespace { 19 20// Device root path constant. 21const char kRootPath[] = "/"; 22 23// Constructs and returns the location of the device using the |storage_name|. 24std::string GetDeviceLocationFromStorageName(const std::string& storage_name) { 25 // Construct a dummy device path using the storage name. This is only used 26 // for registering device media file system. 27 // E.g.: If the |storage_name| is "usb:2,2:12345" then "/usb:2,2:12345" is the 28 // device location. 29 DCHECK(!storage_name.empty()); 30 return kRootPath + storage_name; 31} 32 33// Returns the storage identifier of the device from the given |storage_name|. 34// E.g. If the |storage_name| is "usb:2,2:65537", the storage identifier is 35// "65537". 36std::string GetStorageIdFromStorageName(const std::string& storage_name) { 37 std::vector<std::string> name_parts; 38 base::SplitString(storage_name, ':', &name_parts); 39 return name_parts.size() == 3 ? name_parts[2] : std::string(); 40} 41 42// Returns a unique device id from the given |storage_info|. 43std::string GetDeviceIdFromStorageInfo(const MtpStorageInfo& storage_info) { 44 const std::string storage_id = 45 GetStorageIdFromStorageName(storage_info.storage_name()); 46 if (storage_id.empty()) 47 return std::string(); 48 49 // Some devices have multiple data stores. Therefore, include storage id as 50 // part of unique id along with vendor, model and volume information. 51 const std::string vendor_id = base::UintToString(storage_info.vendor_id()); 52 const std::string model_id = base::UintToString(storage_info.product_id()); 53 return StorageInfo::MakeDeviceId( 54 StorageInfo::MTP_OR_PTP, 55 kVendorModelVolumeStoragePrefix + vendor_id + ":" + model_id + ":" + 56 storage_info.volume_identifier() + ":" + storage_id); 57} 58 59// Returns the |data_store_id| string in the required format. 60// If the |data_store_id| is 65537, this function returns " (65537)". 61std::string GetFormattedIdString(const std::string& data_store_id) { 62 return ("(" + data_store_id + ")"); 63} 64 65// Helper function to get device label from storage information. 66base::string16 GetDeviceLabelFromStorageInfo( 67 const MtpStorageInfo& storage_info) { 68 std::string device_label; 69 const std::string& vendor_name = storage_info.vendor(); 70 device_label = vendor_name; 71 72 const std::string& product_name = storage_info.product(); 73 if (!product_name.empty()) { 74 if (!device_label.empty()) 75 device_label += " "; 76 device_label += product_name; 77 } 78 79 // Add the data store id to the device label. 80 if (!device_label.empty()) { 81 const std::string& volume_id = storage_info.volume_identifier(); 82 if (!volume_id.empty()) { 83 device_label += GetFormattedIdString(volume_id); 84 } else { 85 const std::string data_store_id = 86 GetStorageIdFromStorageName(storage_info.storage_name()); 87 if (!data_store_id.empty()) 88 device_label += GetFormattedIdString(data_store_id); 89 } 90 } 91 return base::UTF8ToUTF16(device_label); 92} 93 94// Helper function to get the device storage details such as device id, label 95// and location. On success and fills in |id|, |label| and |location|. 96void GetStorageInfo(const std::string& storage_name, 97 device::MediaTransferProtocolManager* mtp_manager, 98 std::string* id, 99 base::string16* label, 100 std::string* location) { 101 DCHECK(!storage_name.empty()); 102 const MtpStorageInfo* storage_info = 103 mtp_manager->GetStorageInfo(storage_name); 104 105 if (!storage_info) 106 return; 107 108 *id = GetDeviceIdFromStorageInfo(*storage_info); 109 *label = GetDeviceLabelFromStorageInfo(*storage_info); 110 *location = GetDeviceLocationFromStorageName(storage_name); 111} 112 113} // namespace 114 115MediaTransferProtocolDeviceObserverLinux:: 116MediaTransferProtocolDeviceObserverLinux( 117 StorageMonitor::Receiver* receiver, 118 device::MediaTransferProtocolManager* mtp_manager) 119 : mtp_manager_(mtp_manager), 120 get_storage_info_func_(&GetStorageInfo), 121 notifications_(receiver) { 122 mtp_manager_->AddObserver(this); 123 EnumerateStorages(); 124} 125 126// This constructor is only used by unit tests. 127MediaTransferProtocolDeviceObserverLinux:: 128MediaTransferProtocolDeviceObserverLinux( 129 StorageMonitor::Receiver* receiver, 130 device::MediaTransferProtocolManager* mtp_manager, 131 GetStorageInfoFunc get_storage_info_func) 132 : mtp_manager_(mtp_manager), 133 get_storage_info_func_(get_storage_info_func), 134 notifications_(receiver) { 135} 136 137MediaTransferProtocolDeviceObserverLinux:: 138~MediaTransferProtocolDeviceObserverLinux() { 139 mtp_manager_->RemoveObserver(this); 140} 141 142bool MediaTransferProtocolDeviceObserverLinux::GetStorageInfoForPath( 143 const base::FilePath& path, 144 StorageInfo* storage_info) const { 145 DCHECK(storage_info); 146 147 if (!path.IsAbsolute()) 148 return false; 149 150 std::vector<base::FilePath::StringType> path_components; 151 path.GetComponents(&path_components); 152 if (path_components.size() < 2) 153 return false; 154 155 // First and second component of the path specifies the device location. 156 // E.g.: If |path| is "/usb:2,2:65537/DCIM/Folder_a", "/usb:2,2:65537" is the 157 // device location. 158 StorageLocationToInfoMap::const_iterator info_it = 159 storage_map_.find(GetDeviceLocationFromStorageName(path_components[1])); 160 if (info_it == storage_map_.end()) 161 return false; 162 163 *storage_info = info_it->second; 164 return true; 165} 166 167void MediaTransferProtocolDeviceObserverLinux::EjectDevice( 168 const std::string& device_id, 169 base::Callback<void(StorageMonitor::EjectStatus)> callback) { 170 std::string location; 171 if (!GetLocationForDeviceId(device_id, &location)) { 172 callback.Run(StorageMonitor::EJECT_NO_SUCH_DEVICE); 173 return; 174 } 175 176 // TODO(thestig): Change this to tell the mtp manager to eject the device. 177 178 StorageChanged(false, location); 179 callback.Run(StorageMonitor::EJECT_OK); 180} 181 182// device::MediaTransferProtocolManager::Observer override. 183void MediaTransferProtocolDeviceObserverLinux::StorageChanged( 184 bool is_attached, 185 const std::string& storage_name) { 186 DCHECK(!storage_name.empty()); 187 188 // New storage is attached. 189 if (is_attached) { 190 std::string device_id; 191 base::string16 device_name; 192 std::string location; 193 get_storage_info_func_(storage_name, mtp_manager_, 194 &device_id, &device_name, &location); 195 196 // Keep track of device id and device name to see how often we receive 197 // empty values. 198 MediaStorageUtil::RecordDeviceInfoHistogram(false, device_id, device_name); 199 if (device_id.empty() || device_name.empty()) 200 return; 201 202 DCHECK(!ContainsKey(storage_map_, location)); 203 204 StorageInfo storage_info(device_id, device_name, location, device_name, 205 base::string16(), base::string16(), 0); 206 storage_map_[location] = storage_info; 207 notifications_->ProcessAttach(storage_info); 208 } else { 209 // Existing storage is detached. 210 StorageLocationToInfoMap::iterator it = 211 storage_map_.find(GetDeviceLocationFromStorageName(storage_name)); 212 if (it == storage_map_.end()) 213 return; 214 notifications_->ProcessDetach(it->second.device_id()); 215 storage_map_.erase(it); 216 } 217} 218 219void MediaTransferProtocolDeviceObserverLinux::EnumerateStorages() { 220 typedef std::vector<std::string> StorageList; 221 StorageList storages = mtp_manager_->GetStorages(); 222 for (StorageList::const_iterator storage_iter = storages.begin(); 223 storage_iter != storages.end(); ++storage_iter) { 224 StorageChanged(true, *storage_iter); 225 } 226} 227 228bool MediaTransferProtocolDeviceObserverLinux::GetLocationForDeviceId( 229 const std::string& device_id, std::string* location) const { 230 for (StorageLocationToInfoMap::const_iterator it = storage_map_.begin(); 231 it != storage_map_.end(); ++it) { 232 if (it->second.device_id() == device_id) { 233 *location = it->first; 234 return true; 235 } 236 } 237 238 return false; 239} 240