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// 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 "components/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 "components/storage_monitor/media_storage_util.h" 24#include "components/storage_monitor/removable_device_constants.h" 25#include "components/storage_monitor/storage_info.h" 26#include "content/public/browser/browser_thread.h" 27 28namespace storage_monitor { 29 30namespace { 31 32// Name of the client application that communicates with the MTP device. 33const base::char16 kClientName[] = L"Chromium"; 34 35// Name of the sequenced task runner. 36const char kMediaTaskRunnerName[] = "media-task-runner"; 37 38// Returns true if |data| represents a class of portable devices. 39bool 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. 56base::string16 GetPnpDeviceId(LPARAM data) { 57 DEV_BROADCAST_DEVICEINTERFACE* dev_interface = 58 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data); 59 if (!dev_interface) 60 return base::string16(); 61 base::string16 device_id(dev_interface->dbcc_name); 62 DCHECK(base::IsStringASCII(device_id)); 63 return base::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|. 68bool GetFriendlyName(const base::string16& pnp_device_id, 69 IPortableDeviceManager* device_manager, 70 base::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|. 86bool GetManufacturerName(const base::string16& pnp_device_id, 87 IPortableDeviceManager* device_manager, 88 base::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|. 105bool GetDeviceDescription(const base::string16& pnp_device_id, 106 IPortableDeviceManager* device_manager, 107 base::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. 125bool 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. 149bool SetUp(const base::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|. 173REFPROPERTYKEY GetUniqueIdPropertyKey(const base::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|. 180bool PopulatePropertyKeyCollection( 181 const base::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. 195bool GetStringPropertyValue(IPortableDeviceValues* properties_values, 196 REFPROPERTYKEY key, 197 base::string16* value) { 198 DCHECK(properties_values); 199 DCHECK(value); 200 base::win::ScopedCoMem<base::char16> buffer; 201 HRESULT hr = properties_values->GetStringValue(key, &buffer); 202 if (FAILED(hr)) 203 return false; 204 *value = static_cast<const base::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|. 210bool GetObjectUniqueId(IPortableDevice* device, 211 const base::string16& object_id, 212 base::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|. 246bool ConstructDeviceStorageUniqueId(const base::string16& device_serial_num, 247 const base::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 base::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|. 261bool 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. 300bool IsMassStoragePortableDevice(const base::string16& pnp_device_id, 301 const base::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|. 315base::string16 GetDeviceNameOnBlockingThread( 316 IPortableDeviceManager* portable_device_manager, 317 const base::string16& pnp_device_id) { 318 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 319 DCHECK(portable_device_manager); 320 base::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. 329bool GetDeviceStorageObjectsOnBlockingThread( 330 const base::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 base::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 base::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. 366bool GetDeviceInfoOnBlockingThread( 367 IPortableDeviceManager* portable_device_manager, 368 const base::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. 387bool 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. 404bool 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<base::char16*[]> pnp_device_ids( 419 new base::char16*[pnp_device_count]); 420 hr = portable_device_mgr->GetDevices(pnp_device_ids.get(), &pnp_device_count); 421 if (FAILED(hr)) 422 return false; 423 424 for (DWORD index = 0; index < pnp_device_count; ++index) { 425 PortableDeviceWatcherWin::DeviceDetails device_details; 426 if (GetDeviceInfoOnBlockingThread( 427 portable_device_mgr, pnp_device_ids[index], &device_details)) 428 devices->push_back(device_details); 429 CoTaskMemFree(pnp_device_ids[index]); 430 } 431 return !devices->empty(); 432} 433 434// Handles the device attach event message on a media task runner. 435// |pnp_device_id| specifies the attached plug and play device ID string. On 436// success, returns true and populates |device_details| with device information. 437// On failure, returns false. 438bool HandleDeviceAttachedEventOnBlockingThread( 439 const base::string16& pnp_device_id, 440 PortableDeviceWatcherWin::DeviceDetails* device_details) { 441 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 442 DCHECK(device_details); 443 base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr; 444 if (!GetPortableDeviceManager(&portable_device_mgr)) 445 return false; 446 // Sometimes, portable device manager doesn't have the new device details. 447 // Refresh the manager device list to update its details. 448 portable_device_mgr->RefreshDeviceList(); 449 return GetDeviceInfoOnBlockingThread(portable_device_mgr, pnp_device_id, 450 device_details); 451} 452 453// Registers |hwnd| to receive portable device notification details. On success, 454// returns the device notifications handle else returns NULL. 455HDEVNOTIFY RegisterPortableDeviceNotification(HWND hwnd) { 456 GUID dev_interface_guid = GUID_NULL; 457 HRESULT hr = CLSIDFromString(kWPDDevInterfaceGUID, &dev_interface_guid); 458 if (FAILED(hr)) 459 return NULL; 460 DEV_BROADCAST_DEVICEINTERFACE db = { 461 sizeof(DEV_BROADCAST_DEVICEINTERFACE), 462 DBT_DEVTYP_DEVICEINTERFACE, 463 0, 464 dev_interface_guid 465 }; 466 return RegisterDeviceNotification(hwnd, &db, DEVICE_NOTIFY_WINDOW_HANDLE); 467} 468 469} // namespace 470 471 472// PortableDeviceWatcherWin --------------------------------------------------- 473 474PortableDeviceWatcherWin::DeviceStorageObject::DeviceStorageObject( 475 const base::string16& temporary_id, 476 const std::string& persistent_id) 477 : object_temporary_id(temporary_id), 478 object_persistent_id(persistent_id) { 479} 480 481PortableDeviceWatcherWin::PortableDeviceWatcherWin() 482 : notifications_(NULL), 483 storage_notifications_(NULL), 484 weak_ptr_factory_(this) { 485} 486 487PortableDeviceWatcherWin::~PortableDeviceWatcherWin() { 488 UnregisterDeviceNotification(notifications_); 489} 490 491void PortableDeviceWatcherWin::Init(HWND hwnd) { 492 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 493 notifications_ = RegisterPortableDeviceNotification(hwnd); 494 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); 495 media_task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior( 496 pool->GetNamedSequenceToken(kMediaTaskRunnerName), 497 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); 498 EnumerateAttachedDevices(); 499} 500 501void PortableDeviceWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) { 502 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 503 if (!IsPortableDeviceStructure(data)) 504 return; 505 506 base::string16 device_id = GetPnpDeviceId(data); 507 if (event_type == DBT_DEVICEARRIVAL) 508 HandleDeviceAttachEvent(device_id); 509 else if (event_type == DBT_DEVICEREMOVECOMPLETE) 510 HandleDeviceDetachEvent(device_id); 511} 512 513bool PortableDeviceWatcherWin::GetMTPStorageInfoFromDeviceId( 514 const std::string& storage_device_id, 515 base::string16* device_location, 516 base::string16* storage_object_id) const { 517 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 518 DCHECK(device_location); 519 DCHECK(storage_object_id); 520 MTPStorageMap::const_iterator storage_map_iter = 521 storage_map_.find(storage_device_id); 522 if (storage_map_iter == storage_map_.end()) 523 return false; 524 525 MTPDeviceMap::const_iterator device_iter = 526 device_map_.find(storage_map_iter->second.location()); 527 if (device_iter == device_map_.end()) 528 return false; 529 const StorageObjects& storage_objects = device_iter->second; 530 for (StorageObjects::const_iterator storage_object_iter = 531 storage_objects.begin(); storage_object_iter != storage_objects.end(); 532 ++storage_object_iter) { 533 if (storage_device_id == storage_object_iter->object_persistent_id) { 534 *device_location = storage_map_iter->second.location(); 535 *storage_object_id = storage_object_iter->object_temporary_id; 536 return true; 537 } 538 } 539 return false; 540} 541 542// static 543base::string16 PortableDeviceWatcherWin::GetStoragePathFromStorageId( 544 const std::string& storage_unique_id) { 545 // Construct a dummy device path using the storage name. This is only used 546 // for registering the device media file system. 547 DCHECK(!storage_unique_id.empty()); 548 return base::UTF8ToUTF16("\\\\" + storage_unique_id); 549} 550 551void PortableDeviceWatcherWin::SetNotifications( 552 StorageMonitor::Receiver* notifications) { 553 storage_notifications_ = notifications; 554} 555 556void PortableDeviceWatcherWin::EjectDevice( 557 const std::string& device_id, 558 base::Callback<void(StorageMonitor::EjectStatus)> callback) { 559 // MTP devices on Windows don't have a detach API needed -- signal 560 // the object as if the device is gone and tell the caller it is OK 561 // to remove. 562 base::string16 device_location; // The device_map_ key. 563 base::string16 storage_object_id; 564 if (!GetMTPStorageInfoFromDeviceId(device_id, 565 &device_location, &storage_object_id)) { 566 callback.Run(StorageMonitor::EJECT_NO_SUCH_DEVICE); 567 return; 568 } 569 HandleDeviceDetachEvent(device_location); 570 571 callback.Run(StorageMonitor::EJECT_OK); 572} 573 574void PortableDeviceWatcherWin::EnumerateAttachedDevices() { 575 DCHECK(media_task_runner_.get()); 576 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 577 Devices* devices = new Devices; 578 base::PostTaskAndReplyWithResult( 579 media_task_runner_, 580 FROM_HERE, 581 base::Bind(&EnumerateAttachedDevicesOnBlockingThread, devices), 582 base::Bind(&PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices, 583 weak_ptr_factory_.GetWeakPtr(), base::Owned(devices))); 584} 585 586void PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices( 587 const Devices* devices, const bool result) { 588 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 589 DCHECK(devices); 590 if (!result) 591 return; 592 for (Devices::const_iterator device_iter = devices->begin(); 593 device_iter != devices->end(); ++device_iter) { 594 OnDidHandleDeviceAttachEvent(&(*device_iter), result); 595 } 596} 597 598void PortableDeviceWatcherWin::HandleDeviceAttachEvent( 599 const base::string16& pnp_device_id) { 600 DCHECK(media_task_runner_.get()); 601 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 602 DeviceDetails* device_details = new DeviceDetails; 603 base::PostTaskAndReplyWithResult( 604 media_task_runner_, 605 FROM_HERE, 606 base::Bind(&HandleDeviceAttachedEventOnBlockingThread, pnp_device_id, 607 device_details), 608 base::Bind(&PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent, 609 weak_ptr_factory_.GetWeakPtr(), base::Owned(device_details))); 610} 611 612void PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent( 613 const DeviceDetails* device_details, const bool result) { 614 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 615 DCHECK(device_details); 616 if (!result) 617 return; 618 619 const StorageObjects& storage_objects = device_details->storage_objects; 620 const base::string16& name = device_details->name; 621 const base::string16& location = device_details->location; 622 DCHECK(!ContainsKey(device_map_, location)); 623 for (StorageObjects::const_iterator storage_iter = storage_objects.begin(); 624 storage_iter != storage_objects.end(); ++storage_iter) { 625 const std::string& storage_id = storage_iter->object_persistent_id; 626 DCHECK(!ContainsKey(storage_map_, storage_id)); 627 628 // Keep track of storage id and storage name to see how often we receive 629 // empty values. 630 MediaStorageUtil::RecordDeviceInfoHistogram(false, storage_id, name); 631 if (storage_id.empty() || name.empty()) 632 return; 633 634 // Device can have several data partitions. Therefore, add the 635 // partition identifier to the model name. E.g.: "Nexus 7 (s10001)" 636 base::string16 model_name(name + L" (" + 637 storage_iter->object_temporary_id + L')'); 638 StorageInfo info(storage_id, location, base::string16(), base::string16(), 639 model_name, 0); 640 storage_map_[storage_id] = info; 641 if (storage_notifications_) { 642 info.set_location(GetStoragePathFromStorageId(storage_id)); 643 storage_notifications_->ProcessAttach(info); 644 } 645 } 646 device_map_[location] = storage_objects; 647} 648 649void PortableDeviceWatcherWin::HandleDeviceDetachEvent( 650 const base::string16& pnp_device_id) { 651 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 652 MTPDeviceMap::iterator device_iter = device_map_.find(pnp_device_id); 653 if (device_iter == device_map_.end()) 654 return; 655 656 const StorageObjects& storage_objects = device_iter->second; 657 for (StorageObjects::const_iterator storage_object_iter = 658 storage_objects.begin(); storage_object_iter != storage_objects.end(); 659 ++storage_object_iter) { 660 std::string storage_id = storage_object_iter->object_persistent_id; 661 MTPStorageMap::iterator storage_map_iter = storage_map_.find(storage_id); 662 DCHECK(storage_map_iter != storage_map_.end()); 663 if (storage_notifications_) { 664 storage_notifications_->ProcessDetach( 665 storage_map_iter->second.device_id()); 666 } 667 storage_map_.erase(storage_map_iter); 668 } 669 device_map_.erase(device_iter); 670} 671 672} // namespace storage_monitor 673