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 // Any tasks that communicates with the portable device may take >100ms to 6 // complete. Those tasks should be run on an blocking thread instead of the 7 // UI thread. 8 9 #include "chrome/browser/storage_monitor/portable_device_watcher_win.h" 10 11 #include <dbt.h> 12 #include <portabledevice.h> 13 14 #include "base/files/file_path.h" 15 #include "base/logging.h" 16 #include "base/stl_util.h" 17 #include "base/strings/string_util.h" 18 #include "base/strings/utf_string_conversions.h" 19 #include "base/threading/sequenced_worker_pool.h" 20 #include "base/win/scoped_co_mem.h" 21 #include "base/win/scoped_comptr.h" 22 #include "base/win/scoped_propvariant.h" 23 #include "chrome/browser/storage_monitor/media_storage_util.h" 24 #include "chrome/browser/storage_monitor/removable_device_constants.h" 25 #include "chrome/browser/storage_monitor/storage_info.h" 26 #include "content/public/browser/browser_thread.h" 27 28 namespace chrome { 29 30 namespace { 31 32 // Name of the client application that communicates with the MTP device. 33 const char16 kClientName[] = L"Chromium"; 34 35 // Name of the sequenced task runner. 36 const char kMediaTaskRunnerName[] = "media-task-runner"; 37 38 // Returns true if |data| represents a class of portable devices. 39 bool IsPortableDeviceStructure(LPARAM data) { 40 DEV_BROADCAST_HDR* broadcast_hdr = 41 reinterpret_cast<DEV_BROADCAST_HDR*>(data); 42 if (!broadcast_hdr || 43 (broadcast_hdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)) { 44 return false; 45 } 46 47 GUID guidDevInterface = GUID_NULL; 48 if (FAILED(CLSIDFromString(kWPDDevInterfaceGUID, &guidDevInterface))) 49 return false; 50 DEV_BROADCAST_DEVICEINTERFACE* dev_interface = 51 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data); 52 return (IsEqualGUID(dev_interface->dbcc_classguid, guidDevInterface) != 0); 53 } 54 55 // Returns the portable device plug and play device ID string. 56 string16 GetPnpDeviceId(LPARAM data) { 57 DEV_BROADCAST_DEVICEINTERFACE* dev_interface = 58 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data); 59 if (!dev_interface) 60 return string16(); 61 string16 device_id(dev_interface->dbcc_name); 62 DCHECK(IsStringASCII(device_id)); 63 return StringToLowerASCII(device_id); 64 } 65 66 // Gets the friendly name of the device specified by the |pnp_device_id|. On 67 // success, returns true and fills in |name|. 68 bool GetFriendlyName(const string16& pnp_device_id, 69 IPortableDeviceManager* device_manager, 70 string16* name) { 71 DCHECK(device_manager); 72 DCHECK(name); 73 DWORD name_len = 0; 74 HRESULT hr = device_manager->GetDeviceFriendlyName(pnp_device_id.c_str(), 75 NULL, &name_len); 76 if (FAILED(hr)) 77 return false; 78 79 hr = device_manager->GetDeviceFriendlyName( 80 pnp_device_id.c_str(), WriteInto(name, name_len), &name_len); 81 return (SUCCEEDED(hr) && !name->empty()); 82 } 83 84 // Gets the manufacturer name of the device specified by the |pnp_device_id|. 85 // On success, returns true and fills in |name|. 86 bool GetManufacturerName(const string16& pnp_device_id, 87 IPortableDeviceManager* device_manager, 88 string16* name) { 89 DCHECK(device_manager); 90 DCHECK(name); 91 DWORD name_len = 0; 92 HRESULT hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(), 93 NULL, &name_len); 94 if (FAILED(hr)) 95 return false; 96 97 hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(), 98 WriteInto(name, name_len), 99 &name_len); 100 return (SUCCEEDED(hr) && !name->empty()); 101 } 102 103 // Gets the description of the device specified by the |pnp_device_id|. On 104 // success, returns true and fills in |description|. 105 bool GetDeviceDescription(const string16& pnp_device_id, 106 IPortableDeviceManager* device_manager, 107 string16* description) { 108 DCHECK(device_manager); 109 DCHECK(description); 110 DWORD desc_len = 0; 111 HRESULT hr = device_manager->GetDeviceDescription(pnp_device_id.c_str(), NULL, 112 &desc_len); 113 if (FAILED(hr)) 114 return false; 115 116 hr = device_manager->GetDeviceDescription(pnp_device_id.c_str(), 117 WriteInto(description, desc_len), 118 &desc_len); 119 return (SUCCEEDED(hr) && !description->empty()); 120 } 121 122 // On success, returns true and updates |client_info| with a reference to an 123 // IPortableDeviceValues interface that holds information about the 124 // application that communicates with the device. 125 bool GetClientInformation( 126 base::win::ScopedComPtr<IPortableDeviceValues>* client_info) { 127 HRESULT hr = client_info->CreateInstance(__uuidof(PortableDeviceValues), 128 NULL, CLSCTX_INPROC_SERVER); 129 if (FAILED(hr)) { 130 DPLOG(ERROR) << "Failed to create an instance of IPortableDeviceValues"; 131 return false; 132 } 133 134 // Attempt to set client details. 135 (*client_info)->SetStringValue(WPD_CLIENT_NAME, kClientName); 136 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, 0); 137 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, 0); 138 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_REVISION, 0); 139 (*client_info)->SetUnsignedIntegerValue( 140 WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE, SECURITY_IMPERSONATION); 141 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS, 142 GENERIC_READ); 143 return true; 144 } 145 146 // Opens the device for communication. |pnp_device_id| specifies the plug and 147 // play device ID string. On success, returns true and updates |device| with a 148 // reference to the portable device interface. 149 bool SetUp(const string16& pnp_device_id, 150 base::win::ScopedComPtr<IPortableDevice>* device) { 151 base::win::ScopedComPtr<IPortableDeviceValues> client_info; 152 if (!GetClientInformation(&client_info)) 153 return false; 154 155 HRESULT hr = device->CreateInstance(__uuidof(PortableDevice), NULL, 156 CLSCTX_INPROC_SERVER); 157 if (FAILED(hr)) { 158 DPLOG(ERROR) << "Failed to create an instance of IPortableDevice"; 159 return false; 160 } 161 162 hr = (*device)->Open(pnp_device_id.c_str(), client_info.get()); 163 if (SUCCEEDED(hr)) 164 return true; 165 166 if (hr == E_ACCESSDENIED) 167 DPLOG(ERROR) << "Access denied to open the device"; 168 return false; 169 } 170 171 // Returns the unique id property key of the object specified by the 172 // |object_id|. 173 REFPROPERTYKEY GetUniqueIdPropertyKey(const string16& object_id) { 174 return (object_id == WPD_DEVICE_OBJECT_ID) ? 175 WPD_DEVICE_SERIAL_NUMBER : WPD_OBJECT_PERSISTENT_UNIQUE_ID; 176 } 177 178 // On success, returns true and populates |properties_to_read| with the 179 // property key of the object specified by the |object_id|. 180 bool PopulatePropertyKeyCollection( 181 const string16& object_id, 182 base::win::ScopedComPtr<IPortableDeviceKeyCollection>* properties_to_read) { 183 HRESULT hr = properties_to_read->CreateInstance( 184 __uuidof(PortableDeviceKeyCollection), NULL, CLSCTX_INPROC_SERVER); 185 if (FAILED(hr)) { 186 DPLOG(ERROR) << "Failed to create IPortableDeviceKeyCollection instance"; 187 return false; 188 } 189 REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id); 190 hr = (*properties_to_read)->Add(key); 191 return SUCCEEDED(hr); 192 } 193 194 // Wrapper function to get content property string value. 195 bool GetStringPropertyValue(IPortableDeviceValues* properties_values, 196 REFPROPERTYKEY key, 197 string16* value) { 198 DCHECK(properties_values); 199 DCHECK(value); 200 base::win::ScopedCoMem<char16> buffer; 201 HRESULT hr = properties_values->GetStringValue(key, &buffer); 202 if (FAILED(hr)) 203 return false; 204 *value = static_cast<const char16*>(buffer); 205 return true; 206 } 207 208 // Constructs a unique identifier for the object specified by the |object_id|. 209 // On success, returns true and fills in |unique_id|. 210 bool GetObjectUniqueId(IPortableDevice* device, 211 const string16& object_id, 212 string16* unique_id) { 213 DCHECK(device); 214 DCHECK(unique_id); 215 base::win::ScopedComPtr<IPortableDeviceContent> content; 216 HRESULT hr = device->Content(content.Receive()); 217 if (FAILED(hr)) { 218 DPLOG(ERROR) << "Failed to get IPortableDeviceContent interface"; 219 return false; 220 } 221 222 base::win::ScopedComPtr<IPortableDeviceProperties> properties; 223 hr = content->Properties(properties.Receive()); 224 if (FAILED(hr)) { 225 DPLOG(ERROR) << "Failed to get IPortableDeviceProperties interface"; 226 return false; 227 } 228 229 base::win::ScopedComPtr<IPortableDeviceKeyCollection> properties_to_read; 230 if (!PopulatePropertyKeyCollection(object_id, &properties_to_read)) 231 return false; 232 233 base::win::ScopedComPtr<IPortableDeviceValues> properties_values; 234 if (FAILED(properties->GetValues(object_id.c_str(), 235 properties_to_read.get(), 236 properties_values.Receive()))) { 237 return false; 238 } 239 240 REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id); 241 return GetStringPropertyValue(properties_values.get(), key, unique_id); 242 } 243 244 // Constructs the device storage unique identifier using |device_serial_num| and 245 // |storage_id|. On success, returns true and fills in |device_storage_id|. 246 bool ConstructDeviceStorageUniqueId(const string16& device_serial_num, 247 const string16& storage_id, 248 std::string* device_storage_id) { 249 if (device_serial_num.empty() && storage_id.empty()) 250 return false; 251 252 DCHECK(device_storage_id); 253 *device_storage_id = StorageInfo::MakeDeviceId( 254 StorageInfo::MTP_OR_PTP, 255 UTF16ToUTF8(storage_id + L':' + device_serial_num)); 256 return true; 257 } 258 259 // Gets a list of removable storage object identifiers present in |device|. 260 // On success, returns true and fills in |storage_object_ids|. 261 bool GetRemovableStorageObjectIds( 262 IPortableDevice* device, 263 PortableDeviceWatcherWin::StorageObjectIDs* storage_object_ids) { 264 DCHECK(device); 265 DCHECK(storage_object_ids); 266 base::win::ScopedComPtr<IPortableDeviceCapabilities> capabilities; 267 HRESULT hr = device->Capabilities(capabilities.Receive()); 268 if (FAILED(hr)) { 269 DPLOG(ERROR) << "Failed to get IPortableDeviceCapabilities interface"; 270 return false; 271 } 272 273 base::win::ScopedComPtr<IPortableDevicePropVariantCollection> storage_ids; 274 hr = capabilities->GetFunctionalObjects(WPD_FUNCTIONAL_CATEGORY_STORAGE, 275 storage_ids.Receive()); 276 if (FAILED(hr)) { 277 DPLOG(ERROR) << "Failed to get IPortableDevicePropVariantCollection"; 278 return false; 279 } 280 281 DWORD num_storage_obj_ids = 0; 282 hr = storage_ids->GetCount(&num_storage_obj_ids); 283 if (FAILED(hr)) 284 return false; 285 286 for (DWORD index = 0; index < num_storage_obj_ids; ++index) { 287 base::win::ScopedPropVariant object_id; 288 hr = storage_ids->GetAt(index, object_id.Receive()); 289 if (SUCCEEDED(hr) && object_id.get().vt == VT_LPWSTR && 290 object_id.get().pwszVal != NULL) { 291 storage_object_ids->push_back(object_id.get().pwszVal); 292 } 293 } 294 return true; 295 } 296 297 // Returns true if the portable device belongs to a mass storage class. 298 // |pnp_device_id| specifies the plug and play device id. 299 // |device_name| specifies the name of the device. 300 bool IsMassStoragePortableDevice(const string16& pnp_device_id, 301 const string16& device_name) { 302 // Based on testing, if the pnp device id starts with "\\?\wpdbusenumroot#", 303 // then the attached device belongs to a mass storage class. 304 if (StartsWith(pnp_device_id, L"\\\\?\\wpdbusenumroot#", false)) 305 return true; 306 307 // If the device is a volume mounted device, |device_name| will be 308 // the volume name. 309 return ((device_name.length() >= 2) && (device_name[1] == L':') && 310 (((device_name[0] >= L'A') && (device_name[0] <= L'Z')) || 311 ((device_name[0] >= L'a') && (device_name[0] <= L'z')))); 312 } 313 314 // Returns the name of the device specified by |pnp_device_id|. 315 string16 GetDeviceNameOnBlockingThread( 316 IPortableDeviceManager* portable_device_manager, 317 const string16& pnp_device_id) { 318 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 319 DCHECK(portable_device_manager); 320 string16 name; 321 GetFriendlyName(pnp_device_id, portable_device_manager, &name) || 322 GetDeviceDescription(pnp_device_id, portable_device_manager, &name) || 323 GetManufacturerName(pnp_device_id, portable_device_manager, &name); 324 return name; 325 } 326 327 // Access the device and gets the device storage details. On success, returns 328 // true and populates |storage_objects| with device storage details. 329 bool GetDeviceStorageObjectsOnBlockingThread( 330 const string16& pnp_device_id, 331 PortableDeviceWatcherWin::StorageObjects* storage_objects) { 332 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 333 DCHECK(storage_objects); 334 base::win::ScopedComPtr<IPortableDevice> device; 335 if (!SetUp(pnp_device_id, &device)) 336 return false; 337 338 string16 device_serial_num; 339 if (!GetObjectUniqueId(device.get(), WPD_DEVICE_OBJECT_ID, 340 &device_serial_num)) { 341 return false; 342 } 343 344 PortableDeviceWatcherWin::StorageObjectIDs storage_obj_ids; 345 if (!GetRemovableStorageObjectIds(device.get(), &storage_obj_ids)) 346 return false; 347 for (PortableDeviceWatcherWin::StorageObjectIDs::const_iterator id_iter = 348 storage_obj_ids.begin(); id_iter != storage_obj_ids.end(); ++id_iter) { 349 string16 storage_persistent_id; 350 if (!GetObjectUniqueId(device.get(), *id_iter, &storage_persistent_id)) 351 continue; 352 353 std::string device_storage_id; 354 if (ConstructDeviceStorageUniqueId(device_serial_num, storage_persistent_id, 355 &device_storage_id)) { 356 storage_objects->push_back(PortableDeviceWatcherWin::DeviceStorageObject( 357 *id_iter, device_storage_id)); 358 } 359 } 360 return true; 361 } 362 363 // Accesses the device and gets the device details (name, storage info, etc). 364 // On success returns true and fills in |device_details|. On failure, returns 365 // false. |pnp_device_id| specifies the plug and play device ID string. 366 bool GetDeviceInfoOnBlockingThread( 367 IPortableDeviceManager* portable_device_manager, 368 const string16& pnp_device_id, 369 PortableDeviceWatcherWin::DeviceDetails* device_details) { 370 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 371 DCHECK(portable_device_manager); 372 DCHECK(device_details); 373 DCHECK(!pnp_device_id.empty()); 374 device_details->name = GetDeviceNameOnBlockingThread(portable_device_manager, 375 pnp_device_id); 376 if (IsMassStoragePortableDevice(pnp_device_id, device_details->name)) 377 return false; 378 379 device_details->location = pnp_device_id; 380 PortableDeviceWatcherWin::StorageObjects storage_objects; 381 return GetDeviceStorageObjectsOnBlockingThread( 382 pnp_device_id, &device_details->storage_objects); 383 } 384 385 // Wrapper function to get an instance of portable device manager. On success, 386 // returns true and fills in |portable_device_mgr|. On failure, returns false. 387 bool GetPortableDeviceManager( 388 base::win::ScopedComPtr<IPortableDeviceManager>* portable_device_mgr) { 389 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 390 HRESULT hr = portable_device_mgr->CreateInstance( 391 __uuidof(PortableDeviceManager), NULL, CLSCTX_INPROC_SERVER); 392 if (SUCCEEDED(hr)) 393 return true; 394 395 // Either there is no portable device support (Windows XP with old versions of 396 // Media Player) or the thread does not have COM initialized. 397 DCHECK_NE(CO_E_NOTINITIALIZED, hr); 398 return false; 399 } 400 401 // Enumerates the attached portable devices. On success, returns true and fills 402 // in |devices| with the attached portable device details. On failure, returns 403 // false. 404 bool EnumerateAttachedDevicesOnBlockingThread( 405 PortableDeviceWatcherWin::Devices* devices) { 406 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 407 DCHECK(devices); 408 base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr; 409 if (!GetPortableDeviceManager(&portable_device_mgr)) 410 return false; 411 412 // Get the total number of devices found on the system. 413 DWORD pnp_device_count = 0; 414 HRESULT hr = portable_device_mgr->GetDevices(NULL, &pnp_device_count); 415 if (FAILED(hr)) 416 return false; 417 418 scoped_ptr<char16*[]> pnp_device_ids(new char16*[pnp_device_count]); 419 hr = portable_device_mgr->GetDevices(pnp_device_ids.get(), &pnp_device_count); 420 if (FAILED(hr)) 421 return false; 422 423 for (DWORD index = 0; index < pnp_device_count; ++index) { 424 PortableDeviceWatcherWin::DeviceDetails device_details; 425 if (GetDeviceInfoOnBlockingThread( 426 portable_device_mgr, pnp_device_ids[index], &device_details)) 427 devices->push_back(device_details); 428 CoTaskMemFree(pnp_device_ids[index]); 429 } 430 return !devices->empty(); 431 } 432 433 // Handles the device attach event message on a media task runner. 434 // |pnp_device_id| specifies the attached plug and play device ID string. On 435 // success, returns true and populates |device_details| with device information. 436 // On failure, returns false. 437 bool HandleDeviceAttachedEventOnBlockingThread( 438 const string16& pnp_device_id, 439 PortableDeviceWatcherWin::DeviceDetails* device_details) { 440 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 441 DCHECK(device_details); 442 base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr; 443 if (!GetPortableDeviceManager(&portable_device_mgr)) 444 return false; 445 // Sometimes, portable device manager doesn't have the new device details. 446 // Refresh the manager device list to update its details. 447 portable_device_mgr->RefreshDeviceList(); 448 return GetDeviceInfoOnBlockingThread(portable_device_mgr, pnp_device_id, 449 device_details); 450 } 451 452 // Registers |hwnd| to receive portable device notification details. On success, 453 // returns the device notifications handle else returns NULL. 454 HDEVNOTIFY RegisterPortableDeviceNotification(HWND hwnd) { 455 GUID dev_interface_guid = GUID_NULL; 456 HRESULT hr = CLSIDFromString(kWPDDevInterfaceGUID, &dev_interface_guid); 457 if (FAILED(hr)) 458 return NULL; 459 DEV_BROADCAST_DEVICEINTERFACE db = { 460 sizeof(DEV_BROADCAST_DEVICEINTERFACE), 461 DBT_DEVTYP_DEVICEINTERFACE, 462 0, 463 dev_interface_guid 464 }; 465 return RegisterDeviceNotification(hwnd, &db, DEVICE_NOTIFY_WINDOW_HANDLE); 466 } 467 468 } // namespace 469 470 471 // PortableDeviceWatcherWin --------------------------------------------------- 472 473 PortableDeviceWatcherWin::DeviceStorageObject::DeviceStorageObject( 474 const string16& temporary_id, 475 const std::string& persistent_id) 476 : object_temporary_id(temporary_id), 477 object_persistent_id(persistent_id) { 478 } 479 480 PortableDeviceWatcherWin::PortableDeviceWatcherWin() 481 : notifications_(NULL), 482 storage_notifications_(NULL), 483 weak_ptr_factory_(this) { 484 } 485 486 PortableDeviceWatcherWin::~PortableDeviceWatcherWin() { 487 UnregisterDeviceNotification(notifications_); 488 } 489 490 void PortableDeviceWatcherWin::Init(HWND hwnd) { 491 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 492 notifications_ = RegisterPortableDeviceNotification(hwnd); 493 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); 494 media_task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior( 495 pool->GetNamedSequenceToken(kMediaTaskRunnerName), 496 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); 497 EnumerateAttachedDevices(); 498 } 499 500 void PortableDeviceWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) { 501 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 502 if (!IsPortableDeviceStructure(data)) 503 return; 504 505 string16 device_id = GetPnpDeviceId(data); 506 if (event_type == DBT_DEVICEARRIVAL) 507 HandleDeviceAttachEvent(device_id); 508 else if (event_type == DBT_DEVICEREMOVECOMPLETE) 509 HandleDeviceDetachEvent(device_id); 510 } 511 512 bool PortableDeviceWatcherWin::GetMTPStorageInfoFromDeviceId( 513 const std::string& storage_device_id, 514 string16* device_location, 515 string16* storage_object_id) const { 516 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 517 DCHECK(device_location); 518 DCHECK(storage_object_id); 519 MTPStorageMap::const_iterator storage_map_iter = 520 storage_map_.find(storage_device_id); 521 if (storage_map_iter == storage_map_.end()) 522 return false; 523 524 MTPDeviceMap::const_iterator device_iter = 525 device_map_.find(storage_map_iter->second.location()); 526 if (device_iter == device_map_.end()) 527 return false; 528 const StorageObjects& storage_objects = device_iter->second; 529 for (StorageObjects::const_iterator storage_object_iter = 530 storage_objects.begin(); storage_object_iter != storage_objects.end(); 531 ++storage_object_iter) { 532 if (storage_device_id == storage_object_iter->object_persistent_id) { 533 *device_location = storage_map_iter->second.location(); 534 *storage_object_id = storage_object_iter->object_temporary_id; 535 return true; 536 } 537 } 538 return false; 539 } 540 541 // static 542 string16 PortableDeviceWatcherWin::GetStoragePathFromStorageId( 543 const std::string& storage_unique_id) { 544 // Construct a dummy device path using the storage name. This is only used 545 // for registering the device media file system. 546 DCHECK(!storage_unique_id.empty()); 547 return UTF8ToUTF16("\\\\" + storage_unique_id); 548 } 549 550 void PortableDeviceWatcherWin::SetNotifications( 551 StorageMonitor::Receiver* notifications) { 552 storage_notifications_ = notifications; 553 } 554 555 void PortableDeviceWatcherWin::EjectDevice( 556 const std::string& device_id, 557 base::Callback<void(StorageMonitor::EjectStatus)> callback) { 558 callback.Run(chrome::StorageMonitor::EJECT_FAILURE); 559 } 560 561 void PortableDeviceWatcherWin::EnumerateAttachedDevices() { 562 DCHECK(media_task_runner_.get()); 563 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 564 Devices* devices = new Devices; 565 base::PostTaskAndReplyWithResult( 566 media_task_runner_, 567 FROM_HERE, 568 base::Bind(&EnumerateAttachedDevicesOnBlockingThread, devices), 569 base::Bind(&PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices, 570 weak_ptr_factory_.GetWeakPtr(), base::Owned(devices))); 571 } 572 573 void PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices( 574 const Devices* devices, const bool result) { 575 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 576 DCHECK(devices); 577 if (!result) 578 return; 579 for (Devices::const_iterator device_iter = devices->begin(); 580 device_iter != devices->end(); ++device_iter) { 581 OnDidHandleDeviceAttachEvent(&(*device_iter), result); 582 } 583 } 584 585 void PortableDeviceWatcherWin::HandleDeviceAttachEvent( 586 const string16& pnp_device_id) { 587 DCHECK(media_task_runner_.get()); 588 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 589 DeviceDetails* device_details = new DeviceDetails; 590 base::PostTaskAndReplyWithResult( 591 media_task_runner_, 592 FROM_HERE, 593 base::Bind(&HandleDeviceAttachedEventOnBlockingThread, pnp_device_id, 594 device_details), 595 base::Bind(&PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent, 596 weak_ptr_factory_.GetWeakPtr(), base::Owned(device_details))); 597 } 598 599 void PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent( 600 const DeviceDetails* device_details, const bool result) { 601 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 602 DCHECK(device_details); 603 if (!result) 604 return; 605 606 const StorageObjects& storage_objects = device_details->storage_objects; 607 const string16& name = device_details->name; 608 const string16& location = device_details->location; 609 DCHECK(!ContainsKey(device_map_, location)); 610 for (StorageObjects::const_iterator storage_iter = storage_objects.begin(); 611 storage_iter != storage_objects.end(); ++storage_iter) { 612 const std::string& storage_id = storage_iter->object_persistent_id; 613 DCHECK(!ContainsKey(storage_map_, storage_id)); 614 615 // Keep track of storage id and storage name to see how often we receive 616 // empty values. 617 MediaStorageUtil::RecordDeviceInfoHistogram(false, storage_id, name); 618 if (storage_id.empty() || name.empty()) 619 return; 620 621 // Device can have several data partitions. Therefore, add the 622 // partition identifier to the storage name. E.g.: "Nexus 7 (s10001)" 623 string16 storage_name(name + L" (" + storage_iter->object_temporary_id + 624 L')'); 625 StorageInfo info(storage_id, storage_name, location, 626 string16(), string16(), string16(), 0); 627 storage_map_[storage_id] = info; 628 if (storage_notifications_) { 629 info.set_location(GetStoragePathFromStorageId(storage_id)); 630 storage_notifications_->ProcessAttach(info); 631 } 632 } 633 device_map_[location] = storage_objects; 634 } 635 636 void PortableDeviceWatcherWin::HandleDeviceDetachEvent( 637 const string16& pnp_device_id) { 638 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 639 MTPDeviceMap::iterator device_iter = device_map_.find(pnp_device_id); 640 if (device_iter == device_map_.end()) 641 return; 642 643 const StorageObjects& storage_objects = device_iter->second; 644 for (StorageObjects::const_iterator storage_object_iter = 645 storage_objects.begin(); storage_object_iter != storage_objects.end(); 646 ++storage_object_iter) { 647 std::string storage_id = storage_object_iter->object_persistent_id; 648 MTPStorageMap::iterator storage_map_iter = storage_map_.find(storage_id); 649 DCHECK(storage_map_iter != storage_map_.end()); 650 if (storage_notifications_) { 651 storage_notifications_->ProcessDetach( 652 storage_map_iter->second.device_id()); 653 } 654 storage_map_.erase(storage_map_iter); 655 } 656 device_map_.erase(device_iter); 657 } 658 659 } // namespace chrome 660