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.
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
9 #include "components/storage_monitor/portable_device_watcher_win.h"
12 #include <portabledevice.h>
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"
28 namespace storage_monitor
{
32 // Name of the client application that communicates with the MTP device.
33 const base::char16 kClientName
[] = L
"Chromium";
35 // Name of the sequenced task runner.
36 const char kMediaTaskRunnerName
[] = "media-task-runner";
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
);
43 (broadcast_hdr
->dbch_devicetype
!= DBT_DEVTYP_DEVICEINTERFACE
)) {
47 GUID guidDevInterface
= GUID_NULL
;
48 if (FAILED(CLSIDFromString(kWPDDevInterfaceGUID
, &guidDevInterface
)))
50 DEV_BROADCAST_DEVICEINTERFACE
* dev_interface
=
51 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE
*>(data
);
52 return (IsEqualGUID(dev_interface
->dbcc_classguid
, guidDevInterface
) != 0);
55 // Returns the portable device plug and play device ID string.
56 base::string16
GetPnpDeviceId(LPARAM data
) {
57 DEV_BROADCAST_DEVICEINTERFACE
* dev_interface
=
58 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE
*>(data
);
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
);
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 base::string16
& pnp_device_id
,
69 IPortableDeviceManager
* device_manager
,
70 base::string16
* name
) {
71 DCHECK(device_manager
);
74 HRESULT hr
= device_manager
->GetDeviceFriendlyName(pnp_device_id
.c_str(),
79 hr
= device_manager
->GetDeviceFriendlyName(
80 pnp_device_id
.c_str(), base::WriteInto(name
, name_len
), &name_len
);
81 return (SUCCEEDED(hr
) && !name
->empty());
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 base::string16
& pnp_device_id
,
87 IPortableDeviceManager
* device_manager
,
88 base::string16
* name
) {
89 DCHECK(device_manager
);
92 HRESULT hr
= device_manager
->GetDeviceManufacturer(pnp_device_id
.c_str(),
97 hr
= device_manager
->GetDeviceManufacturer(pnp_device_id
.c_str(),
98 base::WriteInto(name
, name_len
),
100 return (SUCCEEDED(hr
) && !name
->empty());
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 base::string16
& pnp_device_id
,
106 IPortableDeviceManager
* device_manager
,
107 base::string16
* description
) {
108 DCHECK(device_manager
);
111 HRESULT hr
= device_manager
->GetDeviceDescription(pnp_device_id
.c_str(), NULL
,
116 hr
= device_manager
->GetDeviceDescription(
117 pnp_device_id
.c_str(), base::WriteInto(description
, desc_len
), &desc_len
);
118 return (SUCCEEDED(hr
) && !description
->empty());
121 // On success, returns true and updates |client_info| with a reference to an
122 // IPortableDeviceValues interface that holds information about the
123 // application that communicates with the device.
124 bool GetClientInformation(
125 base::win::ScopedComPtr
<IPortableDeviceValues
>* client_info
) {
126 HRESULT hr
= client_info
->CreateInstance(__uuidof(PortableDeviceValues
),
127 NULL
, CLSCTX_INPROC_SERVER
);
129 DPLOG(ERROR
) << "Failed to create an instance of IPortableDeviceValues";
133 // Attempt to set client details.
134 (*client_info
)->SetStringValue(WPD_CLIENT_NAME
, kClientName
);
135 (*client_info
)->SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION
, 0);
136 (*client_info
)->SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION
, 0);
137 (*client_info
)->SetUnsignedIntegerValue(WPD_CLIENT_REVISION
, 0);
138 (*client_info
)->SetUnsignedIntegerValue(
139 WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE
, SECURITY_IMPERSONATION
);
140 (*client_info
)->SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS
,
145 // Opens the device for communication. |pnp_device_id| specifies the plug and
146 // play device ID string. On success, returns true and updates |device| with a
147 // reference to the portable device interface.
148 bool SetUp(const base::string16
& pnp_device_id
,
149 base::win::ScopedComPtr
<IPortableDevice
>* device
) {
150 base::win::ScopedComPtr
<IPortableDeviceValues
> client_info
;
151 if (!GetClientInformation(&client_info
))
154 HRESULT hr
= device
->CreateInstance(__uuidof(PortableDevice
), NULL
,
155 CLSCTX_INPROC_SERVER
);
157 DPLOG(ERROR
) << "Failed to create an instance of IPortableDevice";
161 hr
= (*device
)->Open(pnp_device_id
.c_str(), client_info
.get());
165 if (hr
== E_ACCESSDENIED
)
166 DPLOG(ERROR
) << "Access denied to open the device";
170 // Returns the unique id property key of the object specified by the
172 REFPROPERTYKEY
GetUniqueIdPropertyKey(const base::string16
& object_id
) {
173 return (object_id
== WPD_DEVICE_OBJECT_ID
) ?
174 WPD_DEVICE_SERIAL_NUMBER
: WPD_OBJECT_PERSISTENT_UNIQUE_ID
;
177 // On success, returns true and populates |properties_to_read| with the
178 // property key of the object specified by the |object_id|.
179 bool PopulatePropertyKeyCollection(
180 const base::string16
& object_id
,
181 base::win::ScopedComPtr
<IPortableDeviceKeyCollection
>* properties_to_read
) {
182 HRESULT hr
= properties_to_read
->CreateInstance(
183 __uuidof(PortableDeviceKeyCollection
), NULL
, CLSCTX_INPROC_SERVER
);
185 DPLOG(ERROR
) << "Failed to create IPortableDeviceKeyCollection instance";
188 REFPROPERTYKEY key
= GetUniqueIdPropertyKey(object_id
);
189 hr
= (*properties_to_read
)->Add(key
);
190 return SUCCEEDED(hr
);
193 // Wrapper function to get content property string value.
194 bool GetStringPropertyValue(IPortableDeviceValues
* properties_values
,
196 base::string16
* value
) {
197 DCHECK(properties_values
);
199 base::win::ScopedCoMem
<base::char16
> buffer
;
200 HRESULT hr
= properties_values
->GetStringValue(key
, &buffer
);
203 *value
= static_cast<const base::char16
*>(buffer
);
207 // Constructs a unique identifier for the object specified by the |object_id|.
208 // On success, returns true and fills in |unique_id|.
209 bool GetObjectUniqueId(IPortableDevice
* device
,
210 const base::string16
& object_id
,
211 base::string16
* unique_id
) {
214 base::win::ScopedComPtr
<IPortableDeviceContent
> content
;
215 HRESULT hr
= device
->Content(content
.Receive());
217 DPLOG(ERROR
) << "Failed to get IPortableDeviceContent interface";
221 base::win::ScopedComPtr
<IPortableDeviceProperties
> properties
;
222 hr
= content
->Properties(properties
.Receive());
224 DPLOG(ERROR
) << "Failed to get IPortableDeviceProperties interface";
228 base::win::ScopedComPtr
<IPortableDeviceKeyCollection
> properties_to_read
;
229 if (!PopulatePropertyKeyCollection(object_id
, &properties_to_read
))
232 base::win::ScopedComPtr
<IPortableDeviceValues
> properties_values
;
233 if (FAILED(properties
->GetValues(object_id
.c_str(),
234 properties_to_read
.get(),
235 properties_values
.Receive()))) {
239 REFPROPERTYKEY key
= GetUniqueIdPropertyKey(object_id
);
240 return GetStringPropertyValue(properties_values
.get(), key
, unique_id
);
243 // Constructs the device storage unique identifier using |device_serial_num| and
244 // |storage_id|. On success, returns true and fills in |device_storage_id|.
245 bool ConstructDeviceStorageUniqueId(const base::string16
& device_serial_num
,
246 const base::string16
& storage_id
,
247 std::string
* device_storage_id
) {
248 if (device_serial_num
.empty() && storage_id
.empty())
251 DCHECK(device_storage_id
);
252 *device_storage_id
= StorageInfo::MakeDeviceId(
253 StorageInfo::MTP_OR_PTP
,
254 base::UTF16ToUTF8(storage_id
+ L
':' + device_serial_num
));
258 // Gets a list of removable storage object identifiers present in |device|.
259 // On success, returns true and fills in |storage_object_ids|.
260 bool GetRemovableStorageObjectIds(
261 IPortableDevice
* device
,
262 PortableDeviceWatcherWin::StorageObjectIDs
* storage_object_ids
) {
264 DCHECK(storage_object_ids
);
265 base::win::ScopedComPtr
<IPortableDeviceCapabilities
> capabilities
;
266 HRESULT hr
= device
->Capabilities(capabilities
.Receive());
268 DPLOG(ERROR
) << "Failed to get IPortableDeviceCapabilities interface";
272 base::win::ScopedComPtr
<IPortableDevicePropVariantCollection
> storage_ids
;
273 hr
= capabilities
->GetFunctionalObjects(WPD_FUNCTIONAL_CATEGORY_STORAGE
,
274 storage_ids
.Receive());
276 DPLOG(ERROR
) << "Failed to get IPortableDevicePropVariantCollection";
280 DWORD num_storage_obj_ids
= 0;
281 hr
= storage_ids
->GetCount(&num_storage_obj_ids
);
285 for (DWORD index
= 0; index
< num_storage_obj_ids
; ++index
) {
286 base::win::ScopedPropVariant object_id
;
287 hr
= storage_ids
->GetAt(index
, object_id
.Receive());
288 if (SUCCEEDED(hr
) && object_id
.get().vt
== VT_LPWSTR
&&
289 object_id
.get().pwszVal
!= NULL
) {
290 storage_object_ids
->push_back(object_id
.get().pwszVal
);
296 // Returns true if the portable device belongs to a mass storage class.
297 // |pnp_device_id| specifies the plug and play device id.
298 // |device_name| specifies the name of the device.
299 bool IsMassStoragePortableDevice(const base::string16
& pnp_device_id
,
300 const base::string16
& device_name
) {
301 // Based on testing, if the pnp device id starts with "\\?\wpdbusenumroot#",
302 // then the attached device belongs to a mass storage class.
303 if (base::StartsWith(pnp_device_id
, L
"\\\\?\\wpdbusenumroot#",
304 base::CompareCase::INSENSITIVE_ASCII
))
307 // If the device is a volume mounted device, |device_name| will be
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'))));
314 // Returns the name of the device specified by |pnp_device_id|.
315 base::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
);
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
);
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 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
))
338 base::string16 device_serial_num
;
339 if (!GetObjectUniqueId(device
.get(), WPD_DEVICE_OBJECT_ID
,
340 &device_serial_num
)) {
344 PortableDeviceWatcherWin::StorageObjectIDs storage_obj_ids
;
345 if (!GetRemovableStorageObjectIds(device
.get(), &storage_obj_ids
))
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
))
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
));
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 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
,
376 if (IsMassStoragePortableDevice(pnp_device_id
, device_details
->name
))
379 device_details
->location
= pnp_device_id
;
380 PortableDeviceWatcherWin::StorageObjects storage_objects
;
381 return GetDeviceStorageObjectsOnBlockingThread(
382 pnp_device_id
, &device_details
->storage_objects
);
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
);
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
);
401 // Enumerates the attached portable devices. On success, returns true and fills
402 // in |devices| with the attached portable device details. On failure, returns
404 bool EnumerateAttachedDevicesOnBlockingThread(
405 PortableDeviceWatcherWin::Devices
* devices
) {
406 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
408 base::win::ScopedComPtr
<IPortableDeviceManager
> portable_device_mgr
;
409 if (!GetPortableDeviceManager(&portable_device_mgr
))
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
);
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
);
424 for (DWORD index
= 0; index
< pnp_device_count
; ++index
) {
425 PortableDeviceWatcherWin::DeviceDetails device_details
;
426 if (GetDeviceInfoOnBlockingThread(portable_device_mgr
.get(),
427 pnp_device_ids
[index
], &device_details
))
428 devices
->push_back(device_details
);
429 CoTaskMemFree(pnp_device_ids
[index
]);
431 return !devices
->empty();
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.
438 bool 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
))
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
.get(), pnp_device_id
,
453 // Registers |hwnd| to receive portable device notification details. On success,
454 // returns the device notifications handle else returns NULL.
455 HDEVNOTIFY
RegisterPortableDeviceNotification(HWND hwnd
) {
456 GUID dev_interface_guid
= GUID_NULL
;
457 HRESULT hr
= CLSIDFromString(kWPDDevInterfaceGUID
, &dev_interface_guid
);
460 DEV_BROADCAST_DEVICEINTERFACE db
= {
461 sizeof(DEV_BROADCAST_DEVICEINTERFACE
),
462 DBT_DEVTYP_DEVICEINTERFACE
,
466 return RegisterDeviceNotification(hwnd
, &db
, DEVICE_NOTIFY_WINDOW_HANDLE
);
472 // PortableDeviceWatcherWin ---------------------------------------------------
474 PortableDeviceWatcherWin::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
) {
481 PortableDeviceWatcherWin::DeviceDetails::DeviceDetails() {
484 PortableDeviceWatcherWin::DeviceDetails::~DeviceDetails() {
487 PortableDeviceWatcherWin::PortableDeviceWatcherWin()
488 : notifications_(NULL
),
489 storage_notifications_(NULL
),
490 weak_ptr_factory_(this) {
493 PortableDeviceWatcherWin::~PortableDeviceWatcherWin() {
494 UnregisterDeviceNotification(notifications_
);
497 void PortableDeviceWatcherWin::Init(HWND hwnd
) {
498 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
499 notifications_
= RegisterPortableDeviceNotification(hwnd
);
500 base::SequencedWorkerPool
* pool
= content::BrowserThread::GetBlockingPool();
501 media_task_runner_
= pool
->GetSequencedTaskRunnerWithShutdownBehavior(
502 pool
->GetNamedSequenceToken(kMediaTaskRunnerName
),
503 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN
);
504 EnumerateAttachedDevices();
507 void PortableDeviceWatcherWin::OnWindowMessage(UINT event_type
, LPARAM data
) {
508 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
509 if (!IsPortableDeviceStructure(data
))
512 base::string16 device_id
= GetPnpDeviceId(data
);
513 if (event_type
== DBT_DEVICEARRIVAL
)
514 HandleDeviceAttachEvent(device_id
);
515 else if (event_type
== DBT_DEVICEREMOVECOMPLETE
)
516 HandleDeviceDetachEvent(device_id
);
519 bool PortableDeviceWatcherWin::GetMTPStorageInfoFromDeviceId(
520 const std::string
& storage_device_id
,
521 base::string16
* device_location
,
522 base::string16
* storage_object_id
) const {
523 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
524 DCHECK(device_location
);
525 DCHECK(storage_object_id
);
526 MTPStorageMap::const_iterator storage_map_iter
=
527 storage_map_
.find(storage_device_id
);
528 if (storage_map_iter
== storage_map_
.end())
531 MTPDeviceMap::const_iterator device_iter
=
532 device_map_
.find(storage_map_iter
->second
.location());
533 if (device_iter
== device_map_
.end())
535 const StorageObjects
& storage_objects
= device_iter
->second
;
536 for (StorageObjects::const_iterator storage_object_iter
=
537 storage_objects
.begin(); storage_object_iter
!= storage_objects
.end();
538 ++storage_object_iter
) {
539 if (storage_device_id
== storage_object_iter
->object_persistent_id
) {
540 *device_location
= storage_map_iter
->second
.location();
541 *storage_object_id
= storage_object_iter
->object_temporary_id
;
549 base::string16
PortableDeviceWatcherWin::GetStoragePathFromStorageId(
550 const std::string
& storage_unique_id
) {
551 // Construct a dummy device path using the storage name. This is only used
552 // for registering the device media file system.
553 DCHECK(!storage_unique_id
.empty());
554 return base::UTF8ToUTF16("\\\\" + storage_unique_id
);
557 void PortableDeviceWatcherWin::SetNotifications(
558 StorageMonitor::Receiver
* notifications
) {
559 storage_notifications_
= notifications
;
562 void PortableDeviceWatcherWin::EjectDevice(
563 const std::string
& device_id
,
564 base::Callback
<void(StorageMonitor::EjectStatus
)> callback
) {
565 // MTP devices on Windows don't have a detach API needed -- signal
566 // the object as if the device is gone and tell the caller it is OK
568 base::string16 device_location
; // The device_map_ key.
569 base::string16 storage_object_id
;
570 if (!GetMTPStorageInfoFromDeviceId(device_id
,
571 &device_location
, &storage_object_id
)) {
572 callback
.Run(StorageMonitor::EJECT_NO_SUCH_DEVICE
);
575 HandleDeviceDetachEvent(device_location
);
577 callback
.Run(StorageMonitor::EJECT_OK
);
580 void PortableDeviceWatcherWin::EnumerateAttachedDevices() {
581 DCHECK(media_task_runner_
.get());
582 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
583 Devices
* devices
= new Devices
;
584 base::PostTaskAndReplyWithResult(
585 media_task_runner_
.get(), FROM_HERE
,
586 base::Bind(&EnumerateAttachedDevicesOnBlockingThread
, devices
),
587 base::Bind(&PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices
,
588 weak_ptr_factory_
.GetWeakPtr(), base::Owned(devices
)));
591 void PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices(
592 const Devices
* devices
, const bool result
) {
593 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
597 for (Devices::const_iterator device_iter
= devices
->begin();
598 device_iter
!= devices
->end(); ++device_iter
) {
599 OnDidHandleDeviceAttachEvent(&(*device_iter
), result
);
603 void PortableDeviceWatcherWin::HandleDeviceAttachEvent(
604 const base::string16
& pnp_device_id
) {
605 DCHECK(media_task_runner_
.get());
606 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
607 DeviceDetails
* device_details
= new DeviceDetails
;
608 base::PostTaskAndReplyWithResult(
609 media_task_runner_
.get(), FROM_HERE
,
610 base::Bind(&HandleDeviceAttachedEventOnBlockingThread
, pnp_device_id
,
612 base::Bind(&PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent
,
613 weak_ptr_factory_
.GetWeakPtr(), base::Owned(device_details
)));
616 void PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent(
617 const DeviceDetails
* device_details
, const bool result
) {
618 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
619 DCHECK(device_details
);
623 const StorageObjects
& storage_objects
= device_details
->storage_objects
;
624 const base::string16
& name
= device_details
->name
;
625 const base::string16
& location
= device_details
->location
;
626 DCHECK(!ContainsKey(device_map_
, location
));
627 for (StorageObjects::const_iterator storage_iter
= storage_objects
.begin();
628 storage_iter
!= storage_objects
.end(); ++storage_iter
) {
629 const std::string
& storage_id
= storage_iter
->object_persistent_id
;
630 DCHECK(!ContainsKey(storage_map_
, storage_id
));
632 // Keep track of storage id and storage name to see how often we receive
634 MediaStorageUtil::RecordDeviceInfoHistogram(false, storage_id
, name
);
635 if (storage_id
.empty() || name
.empty())
638 // Device can have several data partitions. Therefore, add the
639 // partition identifier to the model name. E.g.: "Nexus 7 (s10001)"
640 base::string16
model_name(name
+ L
" (" +
641 storage_iter
->object_temporary_id
+ L
')');
642 StorageInfo
info(storage_id
, location
, base::string16(), base::string16(),
644 storage_map_
[storage_id
] = info
;
645 if (storage_notifications_
) {
646 info
.set_location(GetStoragePathFromStorageId(storage_id
));
647 storage_notifications_
->ProcessAttach(info
);
650 device_map_
[location
] = storage_objects
;
653 void PortableDeviceWatcherWin::HandleDeviceDetachEvent(
654 const base::string16
& pnp_device_id
) {
655 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
656 MTPDeviceMap::iterator device_iter
= device_map_
.find(pnp_device_id
);
657 if (device_iter
== device_map_
.end())
660 const StorageObjects
& storage_objects
= device_iter
->second
;
661 for (StorageObjects::const_iterator storage_object_iter
=
662 storage_objects
.begin(); storage_object_iter
!= storage_objects
.end();
663 ++storage_object_iter
) {
664 std::string storage_id
= storage_object_iter
->object_persistent_id
;
665 MTPStorageMap::iterator storage_map_iter
= storage_map_
.find(storage_id
);
666 DCHECK(storage_map_iter
!= storage_map_
.end());
667 if (storage_notifications_
) {
668 storage_notifications_
->ProcessDetach(
669 storage_map_iter
->second
.device_id());
671 storage_map_
.erase(storage_map_iter
);
673 device_map_
.erase(device_iter
);
676 } // namespace storage_monitor