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