[M120 Migration][VD] Enable direct rendering for TVPlus
[platform/framework/web/chromium-efl.git] / components / storage_monitor / portable_device_watcher_win.cc
1 // Copyright 2014 The Chromium Authors
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 <objbase.h>
13 #include <portabledevice.h>
14 #include <wrl/client.h>
15
16 #include "base/containers/contains.h"
17 #include "base/files/file_path.h"
18 #include "base/functional/bind.h"
19 #include "base/logging.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/task/thread_pool.h"
23 #include "base/threading/scoped_blocking_call.h"
24 #include "base/win/scoped_co_mem.h"
25 #include "base/win/scoped_propvariant.h"
26 #include "components/storage_monitor/removable_device_constants.h"
27 #include "components/storage_monitor/storage_info.h"
28 #include "content/public/browser/browser_thread.h"
29
30 namespace storage_monitor {
31
32 namespace {
33
34 // Name of the client application that communicates with the MTP device.
35 const wchar_t kClientName[] = L"Chromium";
36
37 // Returns true if |data| represents a class of portable devices.
38 bool IsPortableDeviceStructure(LPARAM data) {
39   DEV_BROADCAST_HDR* broadcast_hdr =
40       reinterpret_cast<DEV_BROADCAST_HDR*>(data);
41   if (!broadcast_hdr ||
42       (broadcast_hdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)) {
43     return false;
44   }
45
46   GUID guidDevInterface = GUID_NULL;
47   if (FAILED(CLSIDFromString(kWPDDevInterfaceGUID, &guidDevInterface)))
48     return false;
49   DEV_BROADCAST_DEVICEINTERFACE* dev_interface =
50       reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data);
51   return (IsEqualGUID(dev_interface->dbcc_classguid, guidDevInterface) != 0);
52 }
53
54 // Returns the portable device plug and play device ID string.
55 std::wstring GetPnpDeviceId(LPARAM data) {
56   DEV_BROADCAST_DEVICEINTERFACE* dev_interface =
57       reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data);
58   if (!dev_interface)
59     return std::wstring();
60   std::wstring device_id(dev_interface->dbcc_name);
61   DCHECK(base::IsStringASCII(device_id));
62   return base::ToLowerASCII(device_id);
63 }
64
65 // Gets the friendly name of the device specified by the |pnp_device_id|. On
66 // success, returns true and fills in |name|.
67 bool GetFriendlyName(const std::wstring& pnp_device_id,
68                      IPortableDeviceManager* device_manager,
69                      std::wstring* name) {
70   DCHECK(device_manager);
71   DCHECK(name);
72   DWORD name_len = 0;
73   HRESULT hr = device_manager->GetDeviceFriendlyName(pnp_device_id.c_str(),
74                                                      nullptr, &name_len);
75   if (FAILED(hr))
76     return false;
77
78   hr = device_manager->GetDeviceFriendlyName(
79       pnp_device_id.c_str(), base::WriteInto(name, name_len), &name_len);
80   return (SUCCEEDED(hr) && !name->empty());
81 }
82
83 // Gets the manufacturer name of the device specified by the |pnp_device_id|.
84 // On success, returns true and fills in |name|.
85 bool GetManufacturerName(const std::wstring& pnp_device_id,
86                          IPortableDeviceManager* device_manager,
87                          std::wstring* name) {
88   DCHECK(device_manager);
89   DCHECK(name);
90   DWORD name_len = 0;
91   HRESULT hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(),
92                                                      nullptr, &name_len);
93   if (FAILED(hr))
94     return false;
95
96   hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(),
97                                              base::WriteInto(name, name_len),
98                                              &name_len);
99   return (SUCCEEDED(hr) && !name->empty());
100 }
101
102 // Gets the description of the device specified by the |pnp_device_id|. On
103 // success, returns true and fills in |description|.
104 bool GetDeviceDescription(const std::wstring& pnp_device_id,
105                           IPortableDeviceManager* device_manager,
106                           std::wstring* description) {
107   DCHECK(device_manager);
108   DCHECK(description);
109   DWORD desc_len = 0;
110   HRESULT hr = device_manager->GetDeviceDescription(pnp_device_id.c_str(),
111                                                     nullptr, &desc_len);
112   if (FAILED(hr))
113     return false;
114
115   hr = device_manager->GetDeviceDescription(
116       pnp_device_id.c_str(), base::WriteInto(description, desc_len), &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     Microsoft::WRL::ComPtr<IPortableDeviceValues>* client_info) {
125   HRESULT hr =
126       ::CoCreateInstance(__uuidof(PortableDeviceValues), nullptr,
127                          CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&(*client_info)));
128   if (FAILED(hr)) {
129     DPLOG(ERROR) << "Failed to create an instance of IPortableDeviceValues";
130     return false;
131   }
132
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;
143 }
144
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 std::wstring& pnp_device_id,
149            Microsoft::WRL::ComPtr<IPortableDevice>* device) {
150   Microsoft::WRL::ComPtr<IPortableDeviceValues> client_info;
151   if (!GetClientInformation(&client_info))
152     return false;
153
154   HRESULT hr =
155       ::CoCreateInstance(__uuidof(PortableDevice), nullptr,
156                          CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&(*device)));
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 std::wstring& 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 std::wstring& object_id,
182     Microsoft::WRL::ComPtr<IPortableDeviceKeyCollection>* properties_to_read) {
183   HRESULT hr = ::CoCreateInstance(__uuidof(PortableDeviceKeyCollection),
184                                   nullptr, CLSCTX_INPROC_SERVER,
185                                   IID_PPV_ARGS(&(*properties_to_read)));
186   if (FAILED(hr)) {
187     DPLOG(ERROR) << "Failed to create IPortableDeviceKeyCollection instance";
188     return false;
189   }
190   REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id);
191   hr = (*properties_to_read)->Add(key);
192   return SUCCEEDED(hr);
193 }
194
195 // Wrapper function to get content property string value.
196 bool GetStringPropertyValue(IPortableDeviceValues* properties_values,
197                             REFPROPERTYKEY key,
198                             std::wstring* value) {
199   DCHECK(properties_values);
200   DCHECK(value);
201   base::win::ScopedCoMem<wchar_t> buffer;
202   HRESULT hr = properties_values->GetStringValue(key, &buffer);
203   if (FAILED(hr))
204     return false;
205   *value = static_cast<const wchar_t*>(buffer);
206   return true;
207 }
208
209 // Constructs a unique identifier for the object specified by the |object_id|.
210 // On success, returns true and fills in |unique_id|.
211 bool GetObjectUniqueId(IPortableDevice* device,
212                        const std::wstring& object_id,
213                        std::wstring* unique_id) {
214   DCHECK(device);
215   DCHECK(unique_id);
216   Microsoft::WRL::ComPtr<IPortableDeviceContent> content;
217   HRESULT hr = device->Content(&content);
218   if (FAILED(hr)) {
219     DPLOG(ERROR) << "Failed to get IPortableDeviceContent interface";
220     return false;
221   }
222
223   Microsoft::WRL::ComPtr<IPortableDeviceProperties> properties;
224   hr = content->Properties(&properties);
225   if (FAILED(hr)) {
226     DPLOG(ERROR) << "Failed to get IPortableDeviceProperties interface";
227     return false;
228   }
229
230   Microsoft::WRL::ComPtr<IPortableDeviceKeyCollection> properties_to_read;
231   if (!PopulatePropertyKeyCollection(object_id, &properties_to_read))
232     return false;
233
234   Microsoft::WRL::ComPtr<IPortableDeviceValues> properties_values;
235   if (FAILED(properties->GetValues(object_id.c_str(), properties_to_read.Get(),
236                                    &properties_values))) {
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 std::wstring& device_serial_num,
247                                     const std::wstring& 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::WideToUTF8(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   Microsoft::WRL::ComPtr<IPortableDeviceCapabilities> capabilities;
267   HRESULT hr = device->Capabilities(&capabilities);
268   if (FAILED(hr)) {
269     DPLOG(ERROR) << "Failed to get IPortableDeviceCapabilities interface";
270     return false;
271   }
272
273   Microsoft::WRL::ComPtr<IPortableDevicePropVariantCollection> storage_ids;
274   hr = capabilities->GetFunctionalObjects(WPD_FUNCTIONAL_CATEGORY_STORAGE,
275                                           &storage_ids);
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 != nullptr) {
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 std::wstring& pnp_device_id,
301                                  const std::wstring& 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 (base::StartsWith(pnp_device_id, L"\\\\?\\wpdbusenumroot#",
305                        base::CompareCase::INSENSITIVE_ASCII))
306     return true;
307
308   // If the device is a volume mounted device, |device_name| will be
309   // the volume name.
310   return ((device_name.length() >= 2) && (device_name[1] == L':') &&
311       (((device_name[0] >= L'A') && (device_name[0] <= L'Z')) ||
312           ((device_name[0] >= L'a') && (device_name[0] <= L'z'))));
313 }
314
315 // Returns the name of the device specified by |pnp_device_id|.
316 std::wstring GetDeviceNameOnBlockingThread(
317     IPortableDeviceManager* portable_device_manager,
318     const std::wstring& pnp_device_id) {
319   DCHECK(portable_device_manager);
320   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
321                                                 base::BlockingType::MAY_BLOCK);
322   std::wstring name;
323   GetFriendlyName(pnp_device_id, portable_device_manager, &name) ||
324       GetDeviceDescription(pnp_device_id, portable_device_manager, &name) ||
325       GetManufacturerName(pnp_device_id, portable_device_manager, &name);
326   return name;
327 }
328
329 // Access the device and gets the device storage details. On success, returns
330 // true and populates |storage_objects| with device storage details.
331 bool GetDeviceStorageObjectsOnBlockingThread(
332     const std::wstring& pnp_device_id,
333     PortableDeviceWatcherWin::StorageObjects* storage_objects) {
334   DCHECK(storage_objects);
335   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
336                                                 base::BlockingType::MAY_BLOCK);
337   Microsoft::WRL::ComPtr<IPortableDevice> device;
338   if (!SetUp(pnp_device_id, &device))
339     return false;
340
341   std::wstring device_serial_num;
342   if (!GetObjectUniqueId(device.Get(), WPD_DEVICE_OBJECT_ID,
343                          &device_serial_num)) {
344     return false;
345   }
346
347   PortableDeviceWatcherWin::StorageObjectIDs storage_obj_ids;
348   if (!GetRemovableStorageObjectIds(device.Get(), &storage_obj_ids))
349     return false;
350   for (PortableDeviceWatcherWin::StorageObjectIDs::const_iterator id_iter =
351        storage_obj_ids.begin(); id_iter != storage_obj_ids.end(); ++id_iter) {
352     std::wstring storage_persistent_id;
353     if (!GetObjectUniqueId(device.Get(), *id_iter, &storage_persistent_id))
354       continue;
355
356     std::string device_storage_id;
357     if (ConstructDeviceStorageUniqueId(device_serial_num, storage_persistent_id,
358                                        &device_storage_id)) {
359       storage_objects->push_back(PortableDeviceWatcherWin::DeviceStorageObject(
360           *id_iter, device_storage_id));
361     }
362   }
363   return true;
364 }
365
366 // Accesses the device and gets the device details (name, storage info, etc).
367 // On success returns true and fills in |device_details|. On failure, returns
368 // false. |pnp_device_id| specifies the plug and play device ID string.
369 bool GetDeviceInfoOnBlockingThread(
370     IPortableDeviceManager* portable_device_manager,
371     const std::wstring& pnp_device_id,
372     PortableDeviceWatcherWin::DeviceDetails* device_details) {
373   DCHECK(portable_device_manager);
374   DCHECK(device_details);
375   DCHECK(!pnp_device_id.empty());
376   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
377                                                 base::BlockingType::MAY_BLOCK);
378   device_details->name = GetDeviceNameOnBlockingThread(portable_device_manager,
379                                                        pnp_device_id);
380   if (IsMassStoragePortableDevice(pnp_device_id, device_details->name))
381     return false;
382
383   device_details->location = pnp_device_id;
384   PortableDeviceWatcherWin::StorageObjects storage_objects;
385   return GetDeviceStorageObjectsOnBlockingThread(
386       pnp_device_id, &device_details->storage_objects);
387 }
388
389 // Wrapper function to get an instance of portable device manager. On success,
390 // returns true and fills in |portable_device_mgr|. On failure, returns false.
391 bool GetPortableDeviceManager(
392     Microsoft::WRL::ComPtr<IPortableDeviceManager>* portable_device_mgr) {
393   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
394                                                 base::BlockingType::MAY_BLOCK);
395   HRESULT hr = ::CoCreateInstance(__uuidof(PortableDeviceManager), nullptr,
396                                   CLSCTX_INPROC_SERVER,
397                                   IID_PPV_ARGS(&(*portable_device_mgr)));
398   if (SUCCEEDED(hr))
399     return true;
400
401   // Either there is no portable device support (Windows XP with old versions of
402   // Media Player) or the thread does not have COM initialized.
403   DCHECK_NE(CO_E_NOTINITIALIZED, hr);
404   return false;
405 }
406
407 // Enumerates the attached portable devices. On success, returns true and fills
408 // in |devices| with the attached portable device details. On failure, returns
409 // false.
410 bool EnumerateAttachedDevicesOnBlockingThread(
411     PortableDeviceWatcherWin::Devices* devices) {
412   DCHECK(devices);
413   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
414                                                 base::BlockingType::MAY_BLOCK);
415   Microsoft::WRL::ComPtr<IPortableDeviceManager> portable_device_mgr;
416   if (!GetPortableDeviceManager(&portable_device_mgr))
417     return false;
418
419   // Get the total number of devices found on the system.
420   DWORD pnp_device_count = 0;
421   HRESULT hr = portable_device_mgr->GetDevices(nullptr, &pnp_device_count);
422   if (FAILED(hr))
423     return false;
424
425   std::unique_ptr<wchar_t*[]> pnp_device_ids(new wchar_t*[pnp_device_count]);
426   hr = portable_device_mgr->GetDevices(pnp_device_ids.get(), &pnp_device_count);
427   if (FAILED(hr))
428     return false;
429
430   for (DWORD index = 0; index < pnp_device_count; ++index) {
431     PortableDeviceWatcherWin::DeviceDetails device_details;
432     if (GetDeviceInfoOnBlockingThread(portable_device_mgr.Get(),
433                                       pnp_device_ids[index], &device_details))
434       devices->push_back(device_details);
435     CoTaskMemFree(pnp_device_ids[index]);
436   }
437   return !devices->empty();
438 }
439
440 // Handles the device attach event message on a media task runner.
441 // |pnp_device_id| specifies the attached plug and play device ID string. On
442 // success, returns true and populates |device_details| with device information.
443 // On failure, returns false.
444 bool HandleDeviceAttachedEventOnBlockingThread(
445     const std::wstring& pnp_device_id,
446     PortableDeviceWatcherWin::DeviceDetails* device_details) {
447   DCHECK(device_details);
448   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
449                                                 base::BlockingType::MAY_BLOCK);
450   Microsoft::WRL::ComPtr<IPortableDeviceManager> portable_device_mgr;
451   if (!GetPortableDeviceManager(&portable_device_mgr))
452     return false;
453   // Sometimes, portable device manager doesn't have the new device details.
454   // Refresh the manager device list to update its details.
455   portable_device_mgr->RefreshDeviceList();
456   return GetDeviceInfoOnBlockingThread(portable_device_mgr.Get(), pnp_device_id,
457                                        device_details);
458 }
459
460 // Registers |hwnd| to receive portable device notification details. On success,
461 // returns the device notifications handle else returns NULL.
462 HDEVNOTIFY RegisterPortableDeviceNotification(HWND hwnd) {
463   GUID dev_interface_guid = GUID_NULL;
464   HRESULT hr = CLSIDFromString(kWPDDevInterfaceGUID, &dev_interface_guid);
465   if (FAILED(hr))
466     return nullptr;
467   DEV_BROADCAST_DEVICEINTERFACE db = {
468       sizeof(DEV_BROADCAST_DEVICEINTERFACE),
469       DBT_DEVTYP_DEVICEINTERFACE,
470       0,
471       dev_interface_guid
472   };
473   return RegisterDeviceNotification(hwnd, &db, DEVICE_NOTIFY_WINDOW_HANDLE);
474 }
475
476 }  // namespace
477
478
479 // PortableDeviceWatcherWin ---------------------------------------------------
480
481 PortableDeviceWatcherWin::DeviceStorageObject::DeviceStorageObject(
482     const std::wstring& temporary_id,
483     const std::string& persistent_id)
484     : object_temporary_id(temporary_id), object_persistent_id(persistent_id) {}
485
486 PortableDeviceWatcherWin::DeviceDetails::DeviceDetails() {
487 }
488
489 PortableDeviceWatcherWin::DeviceDetails::DeviceDetails(
490     const DeviceDetails& other) = default;
491
492 PortableDeviceWatcherWin::DeviceDetails::~DeviceDetails() {
493 }
494
495 PortableDeviceWatcherWin::PortableDeviceWatcherWin()
496     : notifications_(nullptr), storage_notifications_(nullptr) {}
497
498 PortableDeviceWatcherWin::~PortableDeviceWatcherWin() {
499   UnregisterDeviceNotification(notifications_);
500 }
501
502 void PortableDeviceWatcherWin::Init(HWND hwnd) {
503   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
504   notifications_ = RegisterPortableDeviceNotification(hwnd);
505   media_task_runner_ = base::ThreadPool::CreateCOMSTATaskRunner(
506       {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
507        base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
508   EnumerateAttachedDevices();
509 }
510
511 void PortableDeviceWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) {
512   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
513   if (!IsPortableDeviceStructure(data))
514     return;
515
516   std::wstring device_id = GetPnpDeviceId(data);
517   if (event_type == DBT_DEVICEARRIVAL)
518     HandleDeviceAttachEvent(device_id);
519   else if (event_type == DBT_DEVICEREMOVECOMPLETE)
520     HandleDeviceDetachEvent(device_id);
521 }
522
523 bool PortableDeviceWatcherWin::GetMTPStorageInfoFromDeviceId(
524     const std::string& storage_device_id,
525     std::wstring* device_location,
526     std::wstring* storage_object_id) const {
527   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
528   DCHECK(device_location);
529   DCHECK(storage_object_id);
530   MTPStorageMap::const_iterator storage_map_iter =
531       storage_map_.find(storage_device_id);
532   if (storage_map_iter == storage_map_.end())
533     return false;
534
535   MTPDeviceMap::const_iterator device_iter =
536       device_map_.find(storage_map_iter->second.location());
537   if (device_iter == device_map_.end())
538     return false;
539   const StorageObjects& storage_objects = device_iter->second;
540   for (StorageObjects::const_iterator storage_object_iter =
541        storage_objects.begin(); storage_object_iter != storage_objects.end();
542        ++storage_object_iter) {
543     if (storage_device_id == storage_object_iter->object_persistent_id) {
544       *device_location = storage_map_iter->second.location();
545       *storage_object_id = storage_object_iter->object_temporary_id;
546       return true;
547     }
548   }
549   return false;
550 }
551
552 // static
553 std::wstring PortableDeviceWatcherWin::GetStoragePathFromStorageId(
554     const std::string& storage_unique_id) {
555   // Construct a dummy device path using the storage name. This is only used
556   // for registering the device media file system.
557   DCHECK(!storage_unique_id.empty());
558   return base::UTF8ToWide("\\\\" + storage_unique_id);
559 }
560
561 void PortableDeviceWatcherWin::SetNotifications(
562     StorageMonitor::Receiver* notifications) {
563   storage_notifications_ = notifications;
564 }
565
566 void PortableDeviceWatcherWin::EjectDevice(
567     const std::string& device_id,
568     base::OnceCallback<void(StorageMonitor::EjectStatus)> callback) {
569   // MTP devices on Windows don't have a detach API needed -- signal
570   // the object as if the device is gone and tell the caller it is OK
571   // to remove.
572   std::wstring device_location;  // The device_map_ key.
573   std::wstring storage_object_id;
574   if (!GetMTPStorageInfoFromDeviceId(device_id,
575                                      &device_location, &storage_object_id)) {
576     std::move(callback).Run(StorageMonitor::EJECT_NO_SUCH_DEVICE);
577     return;
578   }
579   HandleDeviceDetachEvent(device_location);
580
581   std::move(callback).Run(StorageMonitor::EJECT_OK);
582 }
583
584 void PortableDeviceWatcherWin::EnumerateAttachedDevices() {
585   DCHECK(media_task_runner_);
586   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
587   Devices* devices = new Devices;
588   media_task_runner_->PostTaskAndReplyWithResult(
589       FROM_HERE,
590       base::BindOnce(&EnumerateAttachedDevicesOnBlockingThread, devices),
591       base::BindOnce(&PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices,
592                      weak_ptr_factory_.GetWeakPtr(), base::Owned(devices)));
593 }
594
595 void PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices(
596     const Devices* devices, const bool result) {
597   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
598   DCHECK(devices);
599   if (!result)
600     return;
601   for (Devices::const_iterator device_iter = devices->begin();
602        device_iter != devices->end(); ++device_iter) {
603     OnDidHandleDeviceAttachEvent(&(*device_iter), result);
604   }
605 }
606
607 void PortableDeviceWatcherWin::HandleDeviceAttachEvent(
608     const std::wstring& pnp_device_id) {
609   DCHECK(media_task_runner_);
610   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
611   DeviceDetails* device_details = new DeviceDetails;
612   media_task_runner_->PostTaskAndReplyWithResult(
613       FROM_HERE,
614       base::BindOnce(&HandleDeviceAttachedEventOnBlockingThread, pnp_device_id,
615                      device_details),
616       base::BindOnce(&PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent,
617                      weak_ptr_factory_.GetWeakPtr(),
618                      base::Owned(device_details)));
619 }
620
621 void PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent(
622     const DeviceDetails* device_details, const bool result) {
623   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
624   DCHECK(device_details);
625   if (!result)
626     return;
627
628   const StorageObjects& storage_objects = device_details->storage_objects;
629   const std::wstring& name = device_details->name;
630   const std::wstring& location = device_details->location;
631   DCHECK(!base::Contains(device_map_, location));
632   for (StorageObjects::const_iterator storage_iter = storage_objects.begin();
633        storage_iter != storage_objects.end(); ++storage_iter) {
634     const std::string& storage_id = storage_iter->object_persistent_id;
635     DCHECK(!base::Contains(storage_map_, storage_id));
636
637     if (storage_id.empty() || name.empty())
638       return;
639
640     // Device can have several data partitions. Therefore, add the
641     // partition identifier to the model name. E.g.: "Nexus 7 (s10001)"
642     std::wstring model_name(name + L" (" + storage_iter->object_temporary_id +
643                             L')');
644     StorageInfo info(storage_id, location, std::u16string(), std::u16string(),
645                      base::WideToUTF16(model_name), 0);
646     storage_map_[storage_id] = info;
647     if (storage_notifications_) {
648       info.set_location(GetStoragePathFromStorageId(storage_id));
649       storage_notifications_->ProcessAttach(info);
650     }
651   }
652   device_map_[location] = storage_objects;
653 }
654
655 void PortableDeviceWatcherWin::HandleDeviceDetachEvent(
656     const std::wstring& pnp_device_id) {
657   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
658   MTPDeviceMap::iterator device_iter = device_map_.find(pnp_device_id);
659   if (device_iter == device_map_.end())
660     return;
661
662   const StorageObjects& storage_objects = device_iter->second;
663   for (StorageObjects::const_iterator storage_object_iter =
664        storage_objects.begin(); storage_object_iter != storage_objects.end();
665        ++storage_object_iter) {
666     std::string storage_id = storage_object_iter->object_persistent_id;
667     MTPStorageMap::iterator storage_map_iter = storage_map_.find(storage_id);
668     DCHECK(storage_map_iter != storage_map_.end());
669     if (storage_notifications_) {
670       storage_notifications_->ProcessDetach(
671           storage_map_iter->second.device_id());
672     }
673     storage_map_.erase(storage_map_iter);
674   }
675   device_map_.erase(device_iter);
676 }
677
678 }  // namespace storage_monitor