Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / storage_monitor / portable_device_watcher_win.cc
blobb57f80a4aaa0975e7041c0e1fabf011865bcf317
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.
9 #include "components/storage_monitor/portable_device_watcher_win.h"
11 #include <dbt.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 {
30 namespace {
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);
42 if (!broadcast_hdr ||
43 (broadcast_hdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)) {
44 return false;
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);
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);
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::ToLowerASCII(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);
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;
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);
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;
97 hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(),
98 base::WriteInto(name, name_len),
99 &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);
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;
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);
128 if (FAILED(hr)) {
129 DPLOG(ERROR) << "Failed to create an instance of IPortableDeviceValues";
130 return false;
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,
141 GENERIC_READ);
142 return true;
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))
152 return false;
154 HRESULT hr = device->CreateInstance(__uuidof(PortableDevice), NULL,
155 CLSCTX_INPROC_SERVER);
156 if (FAILED(hr)) {
157 DPLOG(ERROR) << "Failed to create an instance of IPortableDevice";
158 return false;
161 hr = (*device)->Open(pnp_device_id.c_str(), client_info.get());
162 if (SUCCEEDED(hr))
163 return true;
165 if (hr == E_ACCESSDENIED)
166 DPLOG(ERROR) << "Access denied to open the device";
167 return false;
170 // Returns the unique id property key of the object specified by the
171 // |object_id|.
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);
184 if (FAILED(hr)) {
185 DPLOG(ERROR) << "Failed to create IPortableDeviceKeyCollection instance";
186 return false;
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,
195 REFPROPERTYKEY key,
196 base::string16* value) {
197 DCHECK(properties_values);
198 DCHECK(value);
199 base::win::ScopedCoMem<base::char16> buffer;
200 HRESULT hr = properties_values->GetStringValue(key, &buffer);
201 if (FAILED(hr))
202 return false;
203 *value = static_cast<const base::char16*>(buffer);
204 return true;
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) {
212 DCHECK(device);
213 DCHECK(unique_id);
214 base::win::ScopedComPtr<IPortableDeviceContent> content;
215 HRESULT hr = device->Content(content.Receive());
216 if (FAILED(hr)) {
217 DPLOG(ERROR) << "Failed to get IPortableDeviceContent interface";
218 return false;
221 base::win::ScopedComPtr<IPortableDeviceProperties> properties;
222 hr = content->Properties(properties.Receive());
223 if (FAILED(hr)) {
224 DPLOG(ERROR) << "Failed to get IPortableDeviceProperties interface";
225 return false;
228 base::win::ScopedComPtr<IPortableDeviceKeyCollection> properties_to_read;
229 if (!PopulatePropertyKeyCollection(object_id, &properties_to_read))
230 return false;
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()))) {
236 return false;
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())
249 return false;
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));
255 return true;
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) {
263 DCHECK(device);
264 DCHECK(storage_object_ids);
265 base::win::ScopedComPtr<IPortableDeviceCapabilities> capabilities;
266 HRESULT hr = device->Capabilities(capabilities.Receive());
267 if (FAILED(hr)) {
268 DPLOG(ERROR) << "Failed to get IPortableDeviceCapabilities interface";
269 return false;
272 base::win::ScopedComPtr<IPortableDevicePropVariantCollection> storage_ids;
273 hr = capabilities->GetFunctionalObjects(WPD_FUNCTIONAL_CATEGORY_STORAGE,
274 storage_ids.Receive());
275 if (FAILED(hr)) {
276 DPLOG(ERROR) << "Failed to get IPortableDevicePropVariantCollection";
277 return false;
280 DWORD num_storage_obj_ids = 0;
281 hr = storage_ids->GetCount(&num_storage_obj_ids);
282 if (FAILED(hr))
283 return false;
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);
293 return true;
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))
305 return true;
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'))));
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);
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;
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))
336 return false;
338 base::string16 device_serial_num;
339 if (!GetObjectUniqueId(device.get(), WPD_DEVICE_OBJECT_ID,
340 &device_serial_num)) {
341 return false;
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;
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));
360 return true;
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,
375 pnp_device_id);
376 if (IsMassStoragePortableDevice(pnp_device_id, device_details->name))
377 return false;
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);
392 if (SUCCEEDED(hr))
393 return true;
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;
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;
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;
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;
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))
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.get(), pnp_device_id,
450 device_details);
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);
458 if (FAILED(hr))
459 return NULL;
460 DEV_BROADCAST_DEVICEINTERFACE db = {
461 sizeof(DEV_BROADCAST_DEVICEINTERFACE),
462 DBT_DEVTYP_DEVICEINTERFACE,
464 dev_interface_guid
466 return RegisterDeviceNotification(hwnd, &db, DEVICE_NOTIFY_WINDOW_HANDLE);
469 } // namespace
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))
510 return;
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())
529 return false;
531 MTPDeviceMap::const_iterator device_iter =
532 device_map_.find(storage_map_iter->second.location());
533 if (device_iter == device_map_.end())
534 return false;
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;
542 return true;
545 return false;
548 // static
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
567 // to remove.
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);
573 return;
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);
594 DCHECK(devices);
595 if (!result)
596 return;
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,
611 device_details),
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);
620 if (!result)
621 return;
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
633 // empty values.
634 MediaStorageUtil::RecordDeviceInfoHistogram(false, storage_id, name);
635 if (storage_id.empty() || name.empty())
636 return;
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(),
643 model_name, 0);
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())
658 return;
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