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/storage_monitor_chromeos.h" 6 7 #include "base/files/file_path.h" 8 #include "base/logging.h" 9 #include "base/stl_util.h" 10 #include "base/strings/string16.h" 11 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/string_util.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "components/storage_monitor/media_storage_util.h" 15 #include "components/storage_monitor/media_transfer_protocol_device_observer_linux.h" 16 #include "components/storage_monitor/removable_device_constants.h" 17 #include "content/public/browser/browser_thread.h" 18 #include "device/media_transfer_protocol/media_transfer_protocol_manager.h" 19 20 using chromeos::disks::DiskMountManager; 21 22 namespace storage_monitor { 23 24 namespace { 25 26 // Constructs a device id using uuid or manufacturer (vendor and product) id 27 // details. 28 std::string MakeDeviceUniqueId(const DiskMountManager::Disk& disk) { 29 std::string uuid = disk.fs_uuid(); 30 if (!uuid.empty()) 31 return kFSUniqueIdPrefix + uuid; 32 33 // If one of the vendor or product information is missing, its value in the 34 // string is empty. 35 // Format: VendorModelSerial:VendorInfo:ModelInfo:SerialInfo 36 // TODO(kmadhusu) Extract serial information for the disks and append it to 37 // the device unique id. 38 const std::string& vendor = disk.vendor_id(); 39 const std::string& product = disk.product_id(); 40 if (vendor.empty() && product.empty()) 41 return std::string(); 42 return kVendorModelSerialPrefix + vendor + ":" + product + ":"; 43 } 44 45 // Returns true if the requested device is valid, else false. On success, fills 46 // in |info|. 47 bool GetDeviceInfo(const DiskMountManager::MountPointInfo& mount_info, 48 bool has_dcim, 49 StorageInfo* info) { 50 DCHECK(info); 51 std::string source_path = mount_info.source_path; 52 53 const DiskMountManager::Disk* disk = 54 DiskMountManager::GetInstance()->FindDiskBySourcePath(source_path); 55 if (!disk || disk->device_type() == chromeos::DEVICE_TYPE_UNKNOWN) 56 return false; 57 58 std::string unique_id = MakeDeviceUniqueId(*disk); 59 // Keep track of device uuid and label, to see how often we receive empty 60 // values. 61 base::string16 device_label = base::UTF8ToUTF16(disk->device_label()); 62 MediaStorageUtil::RecordDeviceInfoHistogram(true, unique_id, device_label); 63 if (unique_id.empty()) 64 return false; 65 66 StorageInfo::Type type = has_dcim ? 67 StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM : 68 StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM; 69 70 *info = StorageInfo(StorageInfo::MakeDeviceId(type, unique_id), 71 mount_info.mount_path, 72 device_label, 73 base::UTF8ToUTF16(disk->vendor_name()), 74 base::UTF8ToUTF16(disk->product_name()), 75 disk->total_size_in_bytes()); 76 return true; 77 } 78 79 // Returns whether the mount point in |mount_info| is a media device or not. 80 bool CheckMountedPathOnFileThread( 81 const DiskMountManager::MountPointInfo& mount_info) { 82 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); 83 return MediaStorageUtil::HasDcim(base::FilePath(mount_info.mount_path)); 84 } 85 86 } // namespace 87 88 using content::BrowserThread; 89 90 StorageMonitorCros::StorageMonitorCros() 91 : weak_ptr_factory_(this) { 92 } 93 94 StorageMonitorCros::~StorageMonitorCros() { 95 DiskMountManager* manager = DiskMountManager::GetInstance(); 96 if (manager) { 97 manager->RemoveObserver(this); 98 } 99 } 100 101 void StorageMonitorCros::Init() { 102 DCHECK(DiskMountManager::GetInstance()); 103 DiskMountManager::GetInstance()->AddObserver(this); 104 CheckExistingMountPoints(); 105 106 if (!media_transfer_protocol_manager_) { 107 scoped_refptr<base::MessageLoopProxy> loop_proxy; 108 media_transfer_protocol_manager_.reset( 109 device::MediaTransferProtocolManager::Initialize(loop_proxy)); 110 } 111 112 media_transfer_protocol_device_observer_.reset( 113 new MediaTransferProtocolDeviceObserverLinux( 114 receiver(), media_transfer_protocol_manager_.get())); 115 } 116 117 void StorageMonitorCros::CheckExistingMountPoints() { 118 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 119 const DiskMountManager::MountPointMap& mount_point_map = 120 DiskMountManager::GetInstance()->mount_points(); 121 for (DiskMountManager::MountPointMap::const_iterator it = 122 mount_point_map.begin(); it != mount_point_map.end(); ++it) { 123 BrowserThread::PostTaskAndReplyWithResult( 124 BrowserThread::FILE, FROM_HERE, 125 base::Bind(&CheckMountedPathOnFileThread, it->second), 126 base::Bind(&StorageMonitorCros::AddMountedPath, 127 weak_ptr_factory_.GetWeakPtr(), it->second)); 128 } 129 130 // Note: relies on scheduled tasks on the file thread being sequential. This 131 // block needs to follow the for loop, so that the DoNothing call on the FILE 132 // thread happens after the scheduled metadata retrievals, meaning that the 133 // reply callback will then happen after all the AddNewMount calls. 134 BrowserThread::PostTaskAndReply( 135 BrowserThread::FILE, FROM_HERE, 136 base::Bind(&base::DoNothing), 137 base::Bind(&StorageMonitorCros::MarkInitialized, 138 weak_ptr_factory_.GetWeakPtr())); 139 } 140 141 void StorageMonitorCros::OnDiskEvent(DiskMountManager::DiskEvent event, 142 const DiskMountManager::Disk* disk) {} 143 144 void StorageMonitorCros::OnDeviceEvent(DiskMountManager::DeviceEvent event, 145 const std::string& device_path) {} 146 147 void StorageMonitorCros::OnMountEvent( 148 DiskMountManager::MountEvent event, 149 chromeos::MountError error_code, 150 const DiskMountManager::MountPointInfo& mount_info) { 151 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 152 153 // Ignore mount points that are not devices. 154 if (mount_info.mount_type != chromeos::MOUNT_TYPE_DEVICE) 155 return; 156 // Ignore errors. 157 if (error_code != chromeos::MOUNT_ERROR_NONE) 158 return; 159 if (mount_info.mount_condition != chromeos::disks::MOUNT_CONDITION_NONE) 160 return; 161 162 switch (event) { 163 case DiskMountManager::MOUNTING: { 164 if (ContainsKey(mount_map_, mount_info.mount_path)) { 165 NOTREACHED(); 166 return; 167 } 168 169 BrowserThread::PostTaskAndReplyWithResult( 170 BrowserThread::FILE, FROM_HERE, 171 base::Bind(&CheckMountedPathOnFileThread, mount_info), 172 base::Bind(&StorageMonitorCros::AddMountedPath, 173 weak_ptr_factory_.GetWeakPtr(), mount_info)); 174 break; 175 } 176 case DiskMountManager::UNMOUNTING: { 177 MountMap::iterator it = mount_map_.find(mount_info.mount_path); 178 if (it == mount_map_.end()) 179 return; 180 receiver()->ProcessDetach(it->second.device_id()); 181 mount_map_.erase(it); 182 break; 183 } 184 } 185 } 186 187 void StorageMonitorCros::OnFormatEvent(DiskMountManager::FormatEvent event, 188 chromeos::FormatError error_code, 189 const std::string& device_path) {} 190 191 void StorageMonitorCros::SetMediaTransferProtocolManagerForTest( 192 device::MediaTransferProtocolManager* test_manager) { 193 DCHECK(!media_transfer_protocol_manager_); 194 media_transfer_protocol_manager_.reset(test_manager); 195 } 196 197 198 bool StorageMonitorCros::GetStorageInfoForPath( 199 const base::FilePath& path, 200 StorageInfo* device_info) const { 201 DCHECK(device_info); 202 203 if (media_transfer_protocol_device_observer_->GetStorageInfoForPath( 204 path, device_info)) { 205 return true; 206 } 207 208 if (!path.IsAbsolute()) 209 return false; 210 211 base::FilePath current = path; 212 while (!ContainsKey(mount_map_, current.value()) && 213 current != current.DirName()) { 214 current = current.DirName(); 215 } 216 217 MountMap::const_iterator info_it = mount_map_.find(current.value()); 218 if (info_it == mount_map_.end()) 219 return false; 220 221 *device_info = info_it->second; 222 return true; 223 } 224 225 // Callback executed when the unmount call is run by DiskMountManager. 226 // Forwards result to |EjectDevice| caller. 227 void NotifyUnmountResult( 228 base::Callback<void(StorageMonitor::EjectStatus)> callback, 229 chromeos::MountError error_code) { 230 if (error_code == chromeos::MOUNT_ERROR_NONE) 231 callback.Run(StorageMonitor::EJECT_OK); 232 else 233 callback.Run(StorageMonitor::EJECT_FAILURE); 234 } 235 236 void StorageMonitorCros::EjectDevice( 237 const std::string& device_id, 238 base::Callback<void(EjectStatus)> callback) { 239 StorageInfo::Type type; 240 if (!StorageInfo::CrackDeviceId(device_id, &type, NULL)) { 241 callback.Run(EJECT_FAILURE); 242 return; 243 } 244 245 if (type == StorageInfo::MTP_OR_PTP) { 246 media_transfer_protocol_device_observer_->EjectDevice(device_id, callback); 247 return; 248 } 249 250 std::string mount_path; 251 for (MountMap::const_iterator info_it = mount_map_.begin(); 252 info_it != mount_map_.end(); ++info_it) { 253 if (info_it->second.device_id() == device_id) 254 mount_path = info_it->first; 255 } 256 257 if (mount_path.empty()) { 258 callback.Run(EJECT_NO_SUCH_DEVICE); 259 return; 260 } 261 262 DiskMountManager* manager = DiskMountManager::GetInstance(); 263 if (!manager) { 264 callback.Run(EJECT_FAILURE); 265 return; 266 } 267 268 manager->UnmountPath(mount_path, chromeos::UNMOUNT_OPTIONS_NONE, 269 base::Bind(NotifyUnmountResult, callback)); 270 } 271 272 device::MediaTransferProtocolManager* 273 StorageMonitorCros::media_transfer_protocol_manager() { 274 return media_transfer_protocol_manager_.get(); 275 } 276 277 void StorageMonitorCros::AddMountedPath( 278 const DiskMountManager::MountPointInfo& mount_info, 279 bool has_dcim) { 280 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 281 282 if (ContainsKey(mount_map_, mount_info.mount_path)) { 283 // CheckExistingMountPointsOnUIThread() added the mount point information 284 // in the map before the device attached handler is called. Therefore, an 285 // entry for the device already exists in the map. 286 return; 287 } 288 289 // Get the media device uuid and label if exists. 290 StorageInfo info; 291 if (!GetDeviceInfo(mount_info, has_dcim, &info)) 292 return; 293 294 if (info.device_id().empty()) 295 return; 296 297 mount_map_.insert(std::make_pair(mount_info.mount_path, info)); 298 299 receiver()->ProcessAttach(info); 300 } 301 302 StorageMonitor* StorageMonitor::CreateInternal() { 303 return new StorageMonitorCros(); 304 } 305 306 } // namespace storage_monitor 307