Upstream version 7.36.149.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 storage_monitor {
29
30 namespace {
31
32 // Name of the client application that communicates with the MTP device.
33 const base::char16 kClientName[] = L"Chromium";
34
35 // Name of the sequenced task runner.
36 const char kMediaTaskRunnerName[] = "media-task-runner";
37
38 // Returns true if |data| represents a class of portable devices.
39 bool IsPortableDeviceStructure(LPARAM data) {
40   DEV_BROADCAST_HDR* broadcast_hdr =
41       reinterpret_cast<DEV_BROADCAST_HDR*>(data);
42   if (!broadcast_hdr ||
43       (broadcast_hdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)) {
44     return false;
45   }
46
47   GUID guidDevInterface = GUID_NULL;
48   if (FAILED(CLSIDFromString(kWPDDevInterfaceGUID, &guidDevInterface)))
49     return false;
50   DEV_BROADCAST_DEVICEINTERFACE* dev_interface =
51       reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data);
52   return (IsEqualGUID(dev_interface->dbcc_classguid, guidDevInterface) != 0);
53 }
54
55 // Returns the portable device plug and play device ID string.
56 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 StringToLowerASCII(device_id);
64 }
65
66 // Gets the friendly name of the device specified by the |pnp_device_id|. On
67 // success, returns true and fills in |name|.
68 bool GetFriendlyName(const 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|.
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;
96
97   hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(),
98                                              WriteInto(name, name_len),
99                                              &name_len);
100   return (SUCCEEDED(hr) && !name->empty());
101 }
102
103 // Gets the description of the device specified by the |pnp_device_id|. On
104 // success, returns true and fills in |description|.
105 bool GetDeviceDescription(const 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.
125 bool GetClientInformation(
126     base::win::ScopedComPtr<IPortableDeviceValues>* client_info) {
127   HRESULT hr = client_info->CreateInstance(__uuidof(PortableDeviceValues),
128                                            NULL, CLSCTX_INPROC_SERVER);
129   if (FAILED(hr)) {
130     DPLOG(ERROR) << "Failed to create an instance of IPortableDeviceValues";
131     return false;
132   }
133
134   // Attempt to set client details.
135   (*client_info)->SetStringValue(WPD_CLIENT_NAME, kClientName);
136   (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, 0);
137   (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, 0);
138   (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_REVISION, 0);
139   (*client_info)->SetUnsignedIntegerValue(
140       WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE, SECURITY_IMPERSONATION);
141   (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS,
142                                           GENERIC_READ);
143   return true;
144 }
145
146 // Opens the device for communication. |pnp_device_id| specifies the plug and
147 // play device ID string. On success, returns true and updates |device| with a
148 // reference to the portable device interface.
149 bool SetUp(const 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|.
173 REFPROPERTYKEY 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|.
180 bool 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.
195 bool 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|.
210 bool 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|.
246 bool 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|.
261 bool GetRemovableStorageObjectIds(
262     IPortableDevice* device,
263     PortableDeviceWatcherWin::StorageObjectIDs* storage_object_ids) {
264   DCHECK(device);
265   DCHECK(storage_object_ids);
266   base::win::ScopedComPtr<IPortableDeviceCapabilities> capabilities;
267   HRESULT hr = device->Capabilities(capabilities.Receive());
268   if (FAILED(hr)) {
269     DPLOG(ERROR) << "Failed to get IPortableDeviceCapabilities interface";
270     return false;
271   }
272
273   base::win::ScopedComPtr<IPortableDevicePropVariantCollection> storage_ids;
274   hr = capabilities->GetFunctionalObjects(WPD_FUNCTIONAL_CATEGORY_STORAGE,
275                                           storage_ids.Receive());
276   if (FAILED(hr)) {
277     DPLOG(ERROR) << "Failed to get IPortableDevicePropVariantCollection";
278     return false;
279   }
280
281   DWORD num_storage_obj_ids = 0;
282   hr = storage_ids->GetCount(&num_storage_obj_ids);
283   if (FAILED(hr))
284     return false;
285
286   for (DWORD index = 0; index < num_storage_obj_ids; ++index) {
287     base::win::ScopedPropVariant object_id;
288     hr = storage_ids->GetAt(index, object_id.Receive());
289     if (SUCCEEDED(hr) && object_id.get().vt == VT_LPWSTR &&
290         object_id.get().pwszVal != NULL) {
291       storage_object_ids->push_back(object_id.get().pwszVal);
292     }
293   }
294   return true;
295 }
296
297 // Returns true if the portable device belongs to a mass storage class.
298 // |pnp_device_id| specifies the plug and play device id.
299 // |device_name| specifies the name of the device.
300 bool IsMassStoragePortableDevice(const 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|.
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;
325 }
326
327 // Access the device and gets the device storage details. On success, returns
328 // true and populates |storage_objects| with device storage details.
329 bool GetDeviceStorageObjectsOnBlockingThread(
330     const 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.
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;
378
379   device_details->location = pnp_device_id;
380   PortableDeviceWatcherWin::StorageObjects storage_objects;
381   return GetDeviceStorageObjectsOnBlockingThread(
382       pnp_device_id, &device_details->storage_objects);
383 }
384
385 // Wrapper function to get an instance of portable device manager. On success,
386 // returns true and fills in |portable_device_mgr|. On failure, returns false.
387 bool GetPortableDeviceManager(
388   base::win::ScopedComPtr<IPortableDeviceManager>* portable_device_mgr) {
389   DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
390   HRESULT hr = portable_device_mgr->CreateInstance(
391       __uuidof(PortableDeviceManager), NULL, CLSCTX_INPROC_SERVER);
392   if (SUCCEEDED(hr))
393     return true;
394
395   // Either there is no portable device support (Windows XP with old versions of
396   // Media Player) or the thread does not have COM initialized.
397   DCHECK_NE(CO_E_NOTINITIALIZED, hr);
398   return false;
399 }
400
401 // Enumerates the attached portable devices. On success, returns true and fills
402 // in |devices| with the attached portable device details. On failure, returns
403 // false.
404 bool EnumerateAttachedDevicesOnBlockingThread(
405     PortableDeviceWatcherWin::Devices* devices) {
406   DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
407   DCHECK(devices);
408   base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr;
409   if (!GetPortableDeviceManager(&portable_device_mgr))
410     return false;
411
412   // Get the total number of devices found on the system.
413   DWORD pnp_device_count = 0;
414   HRESULT hr = portable_device_mgr->GetDevices(NULL, &pnp_device_count);
415   if (FAILED(hr))
416     return false;
417
418   scoped_ptr<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.
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, 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.
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,
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
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) {
479 }
480
481 PortableDeviceWatcherWin::PortableDeviceWatcherWin()
482     : notifications_(NULL),
483       storage_notifications_(NULL),
484       weak_ptr_factory_(this) {
485 }
486
487 PortableDeviceWatcherWin::~PortableDeviceWatcherWin() {
488   UnregisterDeviceNotification(notifications_);
489 }
490
491 void 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
501 void 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
513 bool 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
543 base::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
551 void PortableDeviceWatcherWin::SetNotifications(
552     StorageMonitor::Receiver* notifications) {
553   storage_notifications_ = notifications;
554 }
555
556 void 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
574 void 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
586 void 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
598 void 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
612 void 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
649 void 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