OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 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 "chrome/browser/system_monitor/portable_device_watcher_win.h" | |
10 | |
11 #include <dbt.h> | |
12 #include <portabledevice.h> | |
13 | |
14 #include "base/file_path.h" | |
15 #include "base/logging.h" | |
16 #include "base/stl_util.h" | |
17 #include "base/string_util.h" | |
18 #include "base/threading/sequenced_worker_pool.h" | |
19 #include "base/utf_string_conversions.h" | |
20 #include "base/win/scoped_co_mem.h" | |
21 #include "base/win/scoped_comptr.h" | |
22 #include "chrome/browser/system_monitor/media_storage_util.h" | |
23 #include "chrome/browser/system_monitor/removable_device_constants.h" | |
24 #include "content/public/browser/browser_thread.h" | |
25 | |
26 namespace chrome { | |
27 | |
28 namespace { | |
29 | |
30 // Name of the client application that communicates with the MTP device. | |
31 const 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 string16 GetPnpDeviceId(LPARAM data) { | |
55 DEV_BROADCAST_DEVICEINTERFACE* dev_interface = | |
56 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data); | |
57 if (!dev_interface) | |
58 return string16(); | |
59 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 string16& pnp_device_id, | |
67 IPortableDeviceManager* device_manager, | |
68 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 string16& pnp_device_id, | |
85 IPortableDeviceManager* device_manager, | |
86 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 string16& pnp_device_id, | |
104 IPortableDeviceManager* device_manager, | |
105 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 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 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 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 string16* value) { | |
196 DCHECK(properties_values); | |
197 DCHECK(value); | |
198 base::win::ScopedCoMem<char16> buffer; | |
199 HRESULT hr = properties_values->GetStringValue(key, &buffer); | |
200 if (FAILED(hr)) | |
201 return false; | |
202 *value = static_cast<const 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 string16& object_id, | |
210 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 string16& device_serial_num, | |
245 const 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 = MediaStorageUtil::MakeDeviceId( | |
252 MediaStorageUtil::MTP_OR_PTP, | |
253 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 PROPVARIANT object_id = {0}; | |
286 PropVariantInit(&object_id); | |
287 hr = storage_ids->GetAt(index, &object_id); | |
288 if (SUCCEEDED(hr) && (object_id.pwszVal != NULL) && | |
289 (object_id.vt == VT_LPWSTR)) { | |
290 storage_object_ids->push_back(object_id.pwszVal); | |
291 } | |
292 PropVariantClear(&object_id); | |
293 } | |
294 return true; | |
295 } | |
296 | |
297 // Returns true if the portable device is mounted on a volume. |device_name| | |
298 // specifies the name of the device. | |
299 bool IsVolumeMountedPortableDevice(const string16& device_name) { | |
300 // If the device is a volume mounted device, |device_name| will be | |
301 // the volume name. | |
302 return ((device_name.length() >= 2) && (device_name[1] == L':') && | |
303 (((device_name[0] >= L'A') && (device_name[0] <= L'Z')) || | |
304 ((device_name[0] >= L'a') && (device_name[0] <= L'z')))); | |
305 } | |
306 | |
307 // Returns the name of the device specified by |pnp_device_id|. | |
308 string16 GetDeviceNameOnBlockingThread( | |
309 IPortableDeviceManager* portable_device_manager, | |
310 const string16& pnp_device_id) { | |
311 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
312 DCHECK(portable_device_manager); | |
313 string16 name; | |
314 GetFriendlyName(pnp_device_id, portable_device_manager, &name) || | |
315 GetDeviceDescription(pnp_device_id, portable_device_manager, &name) || | |
316 GetManufacturerName(pnp_device_id, portable_device_manager, &name); | |
317 return name; | |
318 } | |
319 | |
320 // Access the device and gets the device storage details. On success, returns | |
321 // true and populates |storage_objects| with device storage details. | |
322 bool GetDeviceStorageObjectsOnBlockingThread( | |
323 const string16& pnp_device_id, | |
324 PortableDeviceWatcherWin::StorageObjects* storage_objects) { | |
325 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
326 DCHECK(storage_objects); | |
327 base::win::ScopedComPtr<IPortableDevice> device; | |
328 if (!SetUp(pnp_device_id, &device)) | |
329 return false; | |
330 | |
331 string16 device_serial_num; | |
332 if (!GetObjectUniqueId(device.get(), WPD_DEVICE_OBJECT_ID, | |
333 &device_serial_num)) { | |
334 return false; | |
335 } | |
336 | |
337 PortableDeviceWatcherWin::StorageObjectIDs storage_obj_ids; | |
338 if (!GetRemovableStorageObjectIds(device.get(), &storage_obj_ids)) | |
339 return false; | |
340 for (PortableDeviceWatcherWin::StorageObjectIDs::const_iterator id_iter = | |
341 storage_obj_ids.begin(); id_iter != storage_obj_ids.end(); ++id_iter) { | |
342 string16 storage_persistent_id; | |
343 if (!GetObjectUniqueId(device.get(), *id_iter, &storage_persistent_id)) | |
344 continue; | |
345 | |
346 std::string device_storage_id; | |
347 if (ConstructDeviceStorageUniqueId(device_serial_num, storage_persistent_id, | |
348 &device_storage_id)) { | |
349 storage_objects->push_back(PortableDeviceWatcherWin::DeviceStorageObject( | |
350 *id_iter, device_storage_id)); | |
351 } | |
352 } | |
353 return true; | |
354 } | |
355 | |
356 // Accesses the device and gets the device details (name, storage info, etc). | |
357 // On success returns true and fills in |device_details|. On failure, returns | |
358 // false. |pnp_device_id| specifies the plug and play device ID string. | |
359 bool GetDeviceInfoOnBlockingThread( | |
360 IPortableDeviceManager* portable_device_manager, | |
361 const string16& pnp_device_id, | |
362 PortableDeviceWatcherWin::DeviceDetails* device_details) { | |
363 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
364 DCHECK(portable_device_manager); | |
365 DCHECK(device_details); | |
366 DCHECK(!pnp_device_id.empty()); | |
367 device_details->name = GetDeviceNameOnBlockingThread(portable_device_manager, | |
368 pnp_device_id); | |
369 if (IsVolumeMountedPortableDevice(device_details->name)) | |
370 return false; | |
371 | |
372 device_details->location = pnp_device_id; | |
373 PortableDeviceWatcherWin::StorageObjects storage_objects; | |
374 return GetDeviceStorageObjectsOnBlockingThread( | |
375 pnp_device_id, &device_details->storage_objects); | |
376 } | |
377 | |
378 // Wrapper function to get an instance of portable device manager. On success, | |
379 // returns true and fills in |portable_device_mgr|. On failure, returns false. | |
380 bool GetPortableDeviceManager( | |
381 base::win::ScopedComPtr<IPortableDeviceManager>* portable_device_mgr) { | |
382 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
383 HRESULT hr = portable_device_mgr->CreateInstance( | |
384 __uuidof(PortableDeviceManager), NULL, CLSCTX_INPROC_SERVER); | |
385 if (SUCCEEDED(hr)) | |
386 return true; | |
387 | |
388 // Either there is no portable device support (Windows XP with old versions of | |
389 // Media Player) or the thread does not have COM initialized. | |
390 DCHECK_NE(CO_E_NOTINITIALIZED, hr); | |
391 return false; | |
392 } | |
393 | |
394 // Enumerates the attached portable devices. On success, returns true and fills | |
395 // in |devices| with the attached portable device details. On failure, returns | |
396 // false. | |
397 bool EnumerateAttachedDevicesOnBlockingThread( | |
398 PortableDeviceWatcherWin::Devices* devices) { | |
399 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
400 DCHECK(devices); | |
401 base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr; | |
402 if (!GetPortableDeviceManager(&portable_device_mgr)) | |
403 return false; | |
404 | |
405 // Get the total number of devices found on the system. | |
406 DWORD pnp_device_count = 0; | |
407 HRESULT hr = portable_device_mgr->GetDevices(NULL, &pnp_device_count); | |
408 if (FAILED(hr)) | |
409 return false; | |
410 | |
411 scoped_array<char16*> pnp_device_ids(new char16*[pnp_device_count]); | |
412 ZeroMemory(pnp_device_ids.get(), pnp_device_count); | |
413 hr = portable_device_mgr->GetDevices(pnp_device_ids.get(), &pnp_device_count); | |
414 if (FAILED(hr)) | |
415 return false; | |
416 | |
417 for (DWORD index = 0; index < pnp_device_count; ++index) { | |
418 PortableDeviceWatcherWin::DeviceDetails device_details; | |
419 if (GetDeviceInfoOnBlockingThread( | |
420 portable_device_mgr, pnp_device_ids[index], &device_details)) | |
421 devices->push_back(device_details); | |
422 } | |
423 return !devices->empty(); | |
424 } | |
425 | |
426 // Handles the device attach event message on a media task runner. | |
427 // |pnp_device_id| specifies the attached plug and play device ID string. On | |
428 // success, returns true and populates |device_details| with device information. | |
429 // On failure, returns false. | |
430 bool HandleDeviceAttachedEventOnBlockingThread( | |
431 const string16& pnp_device_id, | |
432 PortableDeviceWatcherWin::DeviceDetails* device_details) { | |
433 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
434 DCHECK(device_details); | |
435 base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr; | |
436 if (!GetPortableDeviceManager(&portable_device_mgr)) | |
437 return false; | |
438 // Sometimes, portable device manager doesn't have the new device details. | |
439 // Refresh the manager device list to update its details. | |
440 portable_device_mgr->RefreshDeviceList(); | |
441 return GetDeviceInfoOnBlockingThread(portable_device_mgr, pnp_device_id, | |
442 device_details); | |
443 } | |
444 | |
445 // Constructs and returns a storage path from storage unique identifier. | |
446 string16 GetStoragePathFromStorageId(const std::string& storage_unique_id) { | |
447 // Construct a dummy device path using the storage name. This is only used | |
448 // for registering the device media file system. | |
449 DCHECK(!storage_unique_id.empty()); | |
450 return UTF8ToUTF16("\\\\" + storage_unique_id); | |
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 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 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | |
484 } | |
485 | |
486 PortableDeviceWatcherWin::~PortableDeviceWatcherWin() { | |
487 UnregisterDeviceNotification(notifications_); | |
488 } | |
489 | |
490 void PortableDeviceWatcherWin::Init(HWND hwnd) { | |
491 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
492 notifications_ = RegisterPortableDeviceNotification(hwnd); | |
493 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); | |
494 media_task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior( | |
495 pool->GetNamedSequenceToken(kMediaTaskRunnerName), | |
496 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); | |
497 EnumerateAttachedDevices(); | |
498 } | |
499 | |
500 void PortableDeviceWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) { | |
501 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
502 if (!IsPortableDeviceStructure(data)) | |
503 return; | |
504 | |
505 string16 device_id = GetPnpDeviceId(data); | |
506 if (event_type == DBT_DEVICEARRIVAL) | |
507 HandleDeviceAttachEvent(device_id); | |
508 else if (event_type == DBT_DEVICEREMOVECOMPLETE) | |
509 HandleDeviceDetachEvent(device_id); | |
510 } | |
511 | |
512 void PortableDeviceWatcherWin::EnumerateAttachedDevices() { | |
513 DCHECK(media_task_runner_.get()); | |
514 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
515 Devices* devices = new Devices; | |
516 base::PostTaskAndReplyWithResult( | |
517 media_task_runner_, | |
518 FROM_HERE, | |
519 base::Bind(&EnumerateAttachedDevicesOnBlockingThread, devices), | |
520 base::Bind(&PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices, | |
521 weak_ptr_factory_.GetWeakPtr(), base::Owned(devices))); | |
522 } | |
523 | |
524 void PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices( | |
525 const Devices* devices, const bool result) { | |
526 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
527 DCHECK(devices); | |
528 if (!result) | |
529 return; | |
530 for (Devices::const_iterator device_iter = devices->begin(); | |
531 device_iter != devices->end(); ++device_iter) { | |
532 OnDidHandleDeviceAttachEvent(&(*device_iter), result); | |
533 } | |
534 } | |
535 | |
536 void PortableDeviceWatcherWin::HandleDeviceAttachEvent( | |
537 const string16& pnp_device_id) { | |
538 DCHECK(media_task_runner_.get()); | |
539 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
540 DeviceDetails* device_details = new DeviceDetails; | |
541 base::PostTaskAndReplyWithResult( | |
542 media_task_runner_, | |
543 FROM_HERE, | |
544 base::Bind(&HandleDeviceAttachedEventOnBlockingThread, pnp_device_id, | |
545 device_details), | |
546 base::Bind(&PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent, | |
547 weak_ptr_factory_.GetWeakPtr(), base::Owned(device_details))); | |
548 } | |
549 | |
550 void PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent( | |
551 const DeviceDetails* device_details, const bool result) { | |
552 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
553 DCHECK(device_details); | |
554 if (!result) | |
555 return; | |
556 | |
557 const StorageObjects& storage_objects = device_details->storage_objects; | |
558 const string16& name = device_details->name; | |
559 const string16& location = device_details->location; | |
560 DCHECK(!ContainsKey(device_map_, location)); | |
561 base::SystemMonitor* system_monitor = base::SystemMonitor::Get(); | |
562 DCHECK(system_monitor); | |
563 for (StorageObjects::const_iterator storage_iter = storage_objects.begin(); | |
564 storage_iter != storage_objects.end(); ++storage_iter) { | |
565 const std::string& storage_id = storage_iter->object_persistent_id; | |
566 DCHECK(!ContainsKey(storage_map_, storage_id)); | |
567 | |
568 // Keep track of storage id and storage name to see how often we receive | |
569 // empty values. | |
570 MediaStorageUtil::RecordDeviceInfoHistogram(false, storage_id, name); | |
571 if (storage_id.empty() || name.empty()) | |
572 return; | |
573 | |
574 // Device can have several data partitions. Therefore, add the | |
575 // partition identifier to the storage name. E.g.: "Nexus 7 (s10001)" | |
576 string16 storage_name(name + L" (" + storage_iter->object_temporary_id + | |
577 L')'); | |
578 storage_map_[storage_id] = | |
579 base::SystemMonitor::RemovableStorageInfo(storage_id, storage_name, | |
580 location); | |
581 system_monitor->ProcessRemovableStorageAttached( | |
582 storage_id, storage_name, GetStoragePathFromStorageId(storage_id)); | |
583 } | |
584 device_map_[location] = storage_objects; | |
585 } | |
586 | |
587 void PortableDeviceWatcherWin::HandleDeviceDetachEvent( | |
588 const string16& pnp_device_id) { | |
589 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
590 MTPDeviceMap::iterator device_iter = device_map_.find(pnp_device_id); | |
591 if (device_iter == device_map_.end()) | |
592 return; | |
593 | |
594 base::SystemMonitor* system_monitor = base::SystemMonitor::Get(); | |
595 DCHECK(system_monitor); | |
596 | |
597 const StorageObjects& storage_objects = device_iter->second; | |
598 for (StorageObjects::const_iterator storage_object_iter = | |
599 storage_objects.begin(); storage_object_iter != storage_objects.end(); | |
600 ++storage_object_iter) { | |
601 std::string storage_id = storage_object_iter->object_persistent_id; | |
602 MTPStorageMap::iterator storage_iter = storage_map_.find(storage_id); | |
Peter Kasting
2012/10/30 23:58:32
Nit: Might want to name this |storage_map_iter| to
kmadhusu
2012/10/31 00:16:22
Done.
| |
603 DCHECK(storage_iter != storage_map_.end()); | |
604 system_monitor->ProcessRemovableStorageDetached( | |
605 storage_iter->second.device_id); | |
606 storage_map_.erase(storage_iter); | |
607 } | |
608 device_map_.erase(device_iter); | |
609 } | |
610 | |
611 } // namespace chrome | |
OLD | NEW |