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