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 #include "chrome/browser/system_monitor/portable_device_watcher_win.h" | |
6 | |
7 #include <dbt.h> | |
8 #include <portabledevice.h> | |
9 | |
10 #include "base/file_path.h" | |
11 #include "base/logging.h" | |
12 #include "base/stl_util.h" | |
13 #include "base/string_util.h" | |
14 #include "base/threading/sequenced_worker_pool.h" | |
15 #include "base/utf_string_conversions.h" | |
16 #include "base/win/scoped_co_mem.h" | |
17 #include "base/win/scoped_comptr.h" | |
18 #include "chrome/browser/system_monitor/media_storage_util.h" | |
19 #include "chrome/browser/system_monitor/removable_device_constants.h" | |
20 #include "chrome/common/chrome_notification_types.h" | |
21 #include "content/public/browser/browser_thread.h" | |
22 #include "content/public/browser/notification_service.h" | |
23 | |
24 namespace chrome { | |
25 | |
26 namespace { | |
27 | |
28 // Name of the client application that communicates with the mtp device. | |
29 const char16 kClientName[] = L"Chromium"; | |
30 | |
31 // Name of the sequenced task runner. | |
32 const char kMediaTaskRunnerName[] = "media-task-runner"; | |
33 | |
34 // Returns true if |data| represents a class of portable devices. | |
35 bool IsPortableDeviceStructure(LPARAM data) { | |
36 DEV_BROADCAST_HDR* broadcast_hdr = | |
37 reinterpret_cast<DEV_BROADCAST_HDR*>(data); | |
38 if (!broadcast_hdr || | |
39 (broadcast_hdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)) { | |
40 return false; | |
41 } | |
42 | |
43 GUID guidDevInterface = GUID_NULL; | |
44 if (FAILED(CLSIDFromString(kWPDDevInterfaceGUID, &guidDevInterface))) | |
45 return false; | |
46 DEV_BROADCAST_DEVICEINTERFACE* dev_interface = | |
47 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data); | |
48 return (IsEqualGUID(dev_interface->dbcc_classguid, guidDevInterface) != 0); | |
49 } | |
50 | |
51 // Returns the portable device plug and play device ID string. | |
52 string16 GetPnpDeviceId(LPARAM data) { | |
53 DEV_BROADCAST_DEVICEINTERFACE* dev_interface = | |
54 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data); | |
55 if (!dev_interface) | |
56 return string16(); | |
57 DCHECK(IsStringASCII(dev_interface->dbcc_name)); | |
58 return StringToLowerASCII(string16(dev_interface->dbcc_name)); | |
59 } | |
60 | |
61 // Gets the friendly name of the device specified by the |pnp_device_id|. On | |
62 // success, returns true and fills in |name|. | |
63 bool GetFriendlyName(const string16& pnp_device_id, | |
64 IPortableDeviceManager* device_manager, | |
65 string16* name) { | |
66 DCHECK(device_manager); | |
67 DCHECK(name); | |
68 DWORD name_len = 0; | |
69 HRESULT hr = device_manager->GetDeviceFriendlyName(pnp_device_id.c_str(), | |
70 NULL, &name_len); | |
71 if (FAILED(hr)) | |
72 return false; | |
73 | |
74 hr = device_manager->GetDeviceFriendlyName( | |
75 pnp_device_id.c_str(), WriteInto(name, name_len), &name_len); | |
76 return SUCCEEDED(hr) ? !name->empty() : false; | |
77 } | |
78 | |
79 // Gets the manufacturer name of the device specified by the |pnp_device_id|. | |
80 // On success, returns true and fills in |name|. | |
81 bool GetManufacturerName(const string16& pnp_device_id, | |
82 IPortableDeviceManager* device_manager, | |
83 string16* name) { | |
84 DCHECK(device_manager); | |
85 DCHECK(name); | |
86 DWORD name_len = 0; | |
87 HRESULT hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(), | |
88 NULL, &name_len); | |
89 if (FAILED(hr)) | |
90 return false; | |
91 | |
92 hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(), | |
93 WriteInto(name, name_len), | |
94 &name_len); | |
95 return SUCCEEDED(hr) ? !name->empty() : false; | |
96 } | |
97 | |
98 // Gets the description of the device specified by the |pnp_device_id|. On | |
99 // success, returns true and fills in |description|. | |
100 bool GetDeviceDescription(const string16& pnp_device_id, | |
101 IPortableDeviceManager* device_manager, | |
102 string16* description) { | |
103 DCHECK(device_manager); | |
104 DCHECK(description); | |
105 DWORD desc_len = 0; | |
106 HRESULT hr = device_manager->GetDeviceDescription(pnp_device_id.c_str(), NULL, | |
107 &desc_len); | |
108 if (FAILED(hr)) | |
109 return false; | |
110 | |
111 hr = device_manager->GetDeviceDescription(pnp_device_id.c_str(), | |
112 WriteInto(description, desc_len), | |
113 &desc_len); | |
114 return SUCCEEDED(hr) ? !description->empty() : false; | |
115 } | |
116 | |
117 // On success, returns true and updates |client_info| with a reference to an | |
118 // IPortableDeviceValues interface that holds information about the | |
119 // application that communicates with the device. | |
120 bool GetClientInformation( | |
121 base::win::ScopedComPtr<IPortableDeviceValues>* client_info) { | |
122 HRESULT hr = client_info->CreateInstance(__uuidof(PortableDeviceValues), | |
123 NULL, CLSCTX_INPROC_SERVER); | |
124 if (FAILED(hr)) { | |
125 DPLOG(ERROR) << "Failed to create an instance of IPortableDeviceValues"; | |
126 return false; | |
127 } | |
128 | |
129 // Attempt to set client details. | |
130 (*client_info)->SetStringValue(WPD_CLIENT_NAME, kClientName); | |
131 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, 0); | |
132 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, 0); | |
133 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_REVISION, 0); | |
134 (*client_info)->SetUnsignedIntegerValue( | |
135 WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE, SECURITY_IMPERSONATION); | |
136 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS, | |
137 GENERIC_READ); | |
138 return true; | |
139 } | |
140 | |
141 // Opens the device for communication. |pnp_device_id| specifies the plug and | |
142 // play device ID string. On success, returns true and updates |device| with a | |
143 // reference to the portable device interface. | |
144 bool SetUp(const string16& pnp_device_id, | |
145 base::win::ScopedComPtr<IPortableDevice>* device) { | |
146 base::win::ScopedComPtr<IPortableDeviceValues> client_info; | |
147 if (!GetClientInformation(&client_info)) | |
148 return false; | |
149 | |
150 HRESULT hr = device->CreateInstance(__uuidof(PortableDevice), NULL, | |
151 CLSCTX_INPROC_SERVER); | |
152 if (FAILED(hr)) { | |
153 DPLOG(ERROR) << "Failed to create an instance of IPortableDevice"; | |
154 return false; | |
155 } | |
156 | |
157 hr = (*device)->Open(pnp_device_id.c_str(), client_info.get()); | |
158 if (SUCCEEDED(hr)) | |
159 return true; | |
160 | |
161 if (hr == E_ACCESSDENIED) | |
162 DPLOG(ERROR) << "Access denied to open the device"; | |
163 return false; | |
164 } | |
165 | |
166 // Returns the unique id property key of the object specified by the | |
167 // |object_id|. | |
168 REFPROPERTYKEY GetUniqueIdPropertyKey(const string16& object_id) { | |
169 return (object_id == WPD_DEVICE_OBJECT_ID) ? | |
170 WPD_DEVICE_SERIAL_NUMBER : WPD_OBJECT_PERSISTENT_UNIQUE_ID; | |
171 } | |
172 | |
173 // On success, returns true and populates |properties_to_read| with the | |
174 // property key of the object specified by the |object_id|. | |
175 bool PopulatePropertyKeyCollection( | |
176 const string16& object_id, | |
177 base::win::ScopedComPtr<IPortableDeviceKeyCollection>* properties_to_read) { | |
178 HRESULT hr = properties_to_read->CreateInstance( | |
179 __uuidof(PortableDeviceKeyCollection), NULL, CLSCTX_INPROC_SERVER); | |
180 if (FAILED(hr)) { | |
181 DPLOG(ERROR) << "Failed to create IPortableDeviceKeyCollection instance"; | |
182 return false; | |
183 } | |
184 REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id); | |
185 hr = (*properties_to_read)->Add(key); | |
186 return SUCCEEDED(hr); | |
187 } | |
188 | |
189 // Wrapper function to get content property string value. | |
190 bool GetStringPropertyValue(IPortableDeviceValues* properties_values, | |
191 REFPROPERTYKEY key, | |
192 string16* value) { | |
193 DCHECK(properties_values); | |
194 DCHECK(value); | |
195 base::win::ScopedCoMem<char16> buffer; | |
196 HRESULT hr = properties_values->GetStringValue(key, &buffer); | |
197 if (FAILED(hr)) | |
198 return false; | |
199 *value = static_cast<const wchar_t*>(buffer); | |
200 return true; | |
201 } | |
202 | |
203 // Constructs a unique identifier for the object specified by the |object_id|. | |
204 // On success, returns true and fills in |unique_id|. | |
205 bool GetObjectUniqueId(IPortableDevice* device, | |
206 const string16& object_id, | |
207 string16* unique_id) { | |
208 DCHECK(device); | |
209 DCHECK(unique_id); | |
210 base::win::ScopedComPtr<IPortableDeviceContent> content; | |
211 HRESULT hr = device->Content(content.Receive()); | |
212 if (FAILED(hr)) { | |
213 DPLOG(ERROR) << "Failed to get IPortableDeviceContent interface"; | |
214 return false; | |
215 } | |
216 | |
217 base::win::ScopedComPtr<IPortableDeviceProperties> properties; | |
218 hr = content->Properties(properties.Receive()); | |
219 if (FAILED(hr)) { | |
220 DPLOG(ERROR) << "Failed to get IPortableDeviceProperties interface"; | |
221 return false; | |
222 } | |
223 | |
224 base::win::ScopedComPtr<IPortableDeviceKeyCollection> properties_to_read; | |
225 if (!PopulatePropertyKeyCollection(object_id, &properties_to_read)) | |
226 return false; | |
227 | |
228 base::win::ScopedComPtr<IPortableDeviceValues> properties_values; | |
229 if (FAILED(properties->GetValues(object_id.c_str(), | |
230 properties_to_read.get(), | |
231 properties_values.Receive()))) { | |
232 return false; | |
233 } | |
234 | |
235 REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id); | |
236 return GetStringPropertyValue(properties_values.get(), key, unique_id); | |
237 } | |
238 | |
239 // Constructs the device storage unique identifier using |device_serial_num| and | |
240 // |storage_id|. On success, returns true and fills in |device_storage_id|. | |
241 bool ConstructDeviceStorageUniqueId(const string16& device_serial_num, | |
242 const string16& storage_id, | |
243 std::string* device_storage_id) { | |
244 if (device_serial_num.empty() && storage_id.empty()) | |
245 return false; | |
246 | |
247 DCHECK(device_storage_id); | |
248 string16 unique_id(chrome::kMtpDeviceStorageIdPrefix + storage_id + L':' + | |
vandebo (ex-Chrome)
2012/10/26 20:32:10
If there's only one format of device ids for this
kmadhusu
2012/10/26 22:23:26
Done.
| |
249 device_serial_num); | |
250 | |
251 *device_storage_id = MediaStorageUtil::MakeDeviceId( | |
252 MediaStorageUtil::MTP_OR_PTP, UTF16ToUTF8(unique_id)); | |
253 return true; | |
254 } | |
255 | |
256 // Gets a list of removable storage object identifiers present in |device|. | |
257 // On success, returns true and fills in |storage_ids|. | |
258 bool GetRemovableStorageObjectIds(IPortableDevice* device, | |
259 std::vector<string16>* storage_ids) { | |
260 DCHECK(device); | |
261 DCHECK(storage_ids); | |
262 base::win::ScopedComPtr<IPortableDeviceCapabilities> capabilities; | |
263 HRESULT hr = device->Capabilities(capabilities.Receive()); | |
264 if (FAILED(hr)) { | |
265 DPLOG(ERROR) << "Failed to get IPortableDeviceCapabilities interface"; | |
266 return false; | |
267 } | |
268 | |
269 base::win::ScopedComPtr<IPortableDevicePropVariantCollection> storage_obj_ids; | |
270 hr = capabilities->GetFunctionalObjects(WPD_FUNCTIONAL_CATEGORY_STORAGE, | |
271 storage_obj_ids.Receive()); | |
272 if (FAILED(hr)) { | |
273 DPLOG(ERROR) << "Failed to get IPortableDevicePropVariantCollection"; | |
274 return false; | |
275 } | |
276 | |
277 DWORD num_storage_obj_ids = 0; | |
278 hr = storage_obj_ids->GetCount(&num_storage_obj_ids); | |
279 if (FAILED(hr)) | |
280 return false; | |
281 | |
282 for (DWORD index = 0; index < num_storage_obj_ids; ++index) { | |
283 PROPVARIANT object_id = {0}; | |
284 PropVariantInit(&object_id); | |
285 hr = storage_obj_ids->GetAt(index, &object_id); | |
286 if (SUCCEEDED(hr) && (object_id.pwszVal != NULL) && | |
287 (object_id.vt == VT_LPWSTR)) { | |
288 storage_ids->push_back(object_id.pwszVal); | |
289 } | |
290 PropVariantClear(&object_id); | |
291 } | |
292 return true; | |
293 } | |
294 | |
295 // Returns true if the portable device is mounted on a volume. |device_name| | |
296 // specifies the name of the device. | |
297 bool IsVolumeMountedPortableDevice(const string16& device_name) { | |
298 // If the device is a volume mounted device, |device_name| will be | |
299 // the volume name. | |
300 return (device_name.length() >= 2) && (device_name[1] == L':') && | |
301 ((device_name[0] >= L'A') && (device_name[0] <= L'Z') || | |
302 ((device_name[0] >= L'a') && (device_name[0] <= L'z'))); | |
303 } | |
304 | |
305 // Returns the name of the device specified by |pnp_device_id|. | |
306 // Since this task may take a longer time to complete, this function is | |
307 // accessed via the media task runner. | |
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. Since this task may | |
321 // take a longer time to complete, this function is accessed via the media task | |
322 // runner. On success, returns true and populates |storage_objects| with device | |
323 // storage details. | |
324 bool GetDeviceStorageObjectsOnBlockingThread( | |
325 const string16& pnp_device_id, | |
326 PortableDeviceWatcherWin::StorageObjects* storage_objects) { | |
327 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
328 DCHECK(storage_objects); | |
329 base::win::ScopedComPtr<IPortableDevice> device; | |
330 if (!SetUp(pnp_device_id, &device)) | |
331 return false; | |
332 | |
333 string16 device_serial_num; | |
334 if (!GetObjectUniqueId(device.get(), WPD_DEVICE_OBJECT_ID, | |
335 &device_serial_num)) { | |
336 return false; | |
337 } | |
338 | |
339 std::vector<string16> storage_obj_ids; | |
340 if (!GetRemovableStorageObjectIds(device.get(), &storage_obj_ids)) | |
341 return false; | |
342 | |
343 for (std::vector<string16>::const_iterator id_iter = storage_obj_ids.begin(); | |
344 id_iter != storage_obj_ids.end(); ++id_iter) { | |
345 string16 storage_persistent_id; | |
346 if (!GetObjectUniqueId(device.get(), *id_iter, &storage_persistent_id)) | |
347 continue; | |
348 | |
349 std::string device_storage_id; | |
350 if (ConstructDeviceStorageUniqueId(device_serial_num, storage_persistent_id, | |
351 &device_storage_id)) { | |
352 PortableDeviceWatcherWin::DeviceStorageObject storage_object; | |
353 storage_object.object_temporary_id = *id_iter; | |
354 storage_object.object_persistent_id = device_storage_id; | |
355 storage_objects->push_back(storage_object); | |
356 } | |
357 } | |
358 return true; | |
359 } | |
360 | |
361 // Access the device and gets the device details (name, storage info, etc.,). | |
362 // Since this task may take a longer time to complete, this function is | |
363 // accessed via the media task runner. On success returns true and fills in | |
364 // |device_details|. |pnp_device_id| specifies the plug and play device ID | |
365 // string. | |
366 bool GetDeviceInfoOnBlockingThread( | |
367 IPortableDeviceManager* portable_device_manager, | |
368 const 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 | |
375 device_details->name = GetDeviceNameOnBlockingThread(portable_device_manager, | |
376 pnp_device_id); | |
377 if (IsVolumeMountedPortableDevice(device_details->name)) | |
378 return false; | |
379 | |
380 device_details->location = pnp_device_id; | |
381 PortableDeviceWatcherWin::StorageObjects storage_objects; | |
382 return GetDeviceStorageObjectsOnBlockingThread( | |
383 pnp_device_id, &device_details->storage_objects); | |
384 } | |
385 | |
386 // Wrapper function to get an instance of portable device manager. This function | |
387 // may take a long time to complete. Therefore, access this function via a media | |
388 // task runner. On success, returns true and fills in |portable_device_mgr|. | |
389 bool GetPortableDeviceManager( | |
390 base::win::ScopedComPtr<IPortableDeviceManager>* portable_device_mgr) { | |
391 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
392 HRESULT hr = portable_device_mgr->CreateInstance( | |
393 __uuidof(PortableDeviceManager), NULL, CLSCTX_INPROC_SERVER); | |
394 if (SUCCEEDED(hr)) | |
395 return true; | |
396 | |
397 // Either there is no portable device support (it must be a XP with Windows | |
398 // Media Player Version < 10) or the thread does not have COM initialized. | |
399 DCHECK_NE(CO_E_NOTINITIALIZED, hr); | |
400 return false; | |
401 } | |
402 | |
403 // Enumerates the attached portable devices. This function accesses the device | |
404 // to get the device details. Since this task may take a longer time to | |
405 // complete, this function is accessed via the media task runner. On success, | |
406 // fills in |device_details_vector| with the attached portable device details. | |
407 void EnumerateAttachedDevicesOnBlockingThread( | |
408 PortableDeviceWatcherWin::DeviceDetailsVector* device_details_vector) { | |
409 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
410 DCHECK(device_details_vector); | |
411 base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr; | |
412 if (!GetPortableDeviceManager(&portable_device_mgr)) | |
413 return; | |
414 | |
415 // Get the total number of devices found on the system. | |
416 DWORD pnp_device_count = 0; | |
417 HRESULT hr = portable_device_mgr->GetDevices(NULL, &pnp_device_count); | |
418 if (FAILED(hr)) | |
419 return; | |
420 | |
421 scoped_array<LPWSTR> pnp_device_ids(new LPWSTR[pnp_device_count]); | |
422 ZeroMemory(pnp_device_ids.get(), pnp_device_count); | |
423 hr = portable_device_mgr->GetDevices(pnp_device_ids.get(), &pnp_device_count); | |
424 if (FAILED(hr)) | |
425 return; | |
426 | |
427 for (DWORD index = 0; index < pnp_device_count; ++index) { | |
428 PortableDeviceWatcherWin::DeviceDetails device_details; | |
429 if (!GetDeviceInfoOnBlockingThread(portable_device_mgr, | |
430 pnp_device_ids[index], | |
431 &device_details)) { | |
432 continue; | |
433 } | |
434 device_details_vector->push_back(device_details); | |
435 } | |
436 } | |
437 | |
438 // Handles the device attach event message on a media task runner. This | |
439 // function communicates with the device to get the storage details. This task | |
440 // may take a longer time. Therefore, access this function on a media task | |
441 // runner. |pnp_device_id| specifies the attached plug and play device ID | |
442 // string. On success, populates |device_details| with device information. | |
443 void HandleDeviceAttachedEventOnBlockingThread( | |
444 const string16& pnp_device_id, | |
445 PortableDeviceWatcherWin::DeviceDetails* device_details) { | |
446 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
447 DCHECK(device_details); | |
448 base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr; | |
449 if (!GetPortableDeviceManager(&portable_device_mgr)) | |
450 return; | |
451 portable_device_mgr->RefreshDeviceList(); | |
452 GetDeviceInfoOnBlockingThread(portable_device_mgr, pnp_device_id, | |
453 device_details); | |
454 } | |
455 | |
456 // Constructs and returns a storage path from storage unique identifier. | |
457 string16 GetStoragePathFromStorageId(const std::string& storage_unique_id) { | |
458 // Construct a dummy device path using the storage name. This is only used | |
459 // for registering the device media file system. | |
460 DCHECK(!storage_unique_id.empty()); | |
461 const std::string root_path("\\\\"); | |
462 return UTF8ToUTF16(root_path + storage_unique_id); | |
463 } | |
464 | |
465 } // namespace | |
466 | |
467 | |
468 /////////////////////////////////////////////////////////////////////////////// | |
469 // PortableDeviceWatcherWin implementation // | |
470 /////////////////////////////////////////////////////////////////////////////// | |
471 PortableDeviceWatcherWin::PortableDeviceWatcherWin() | |
472 : app_terminating_(false), | |
473 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | |
474 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, | |
475 content::NotificationService::AllSources()); | |
476 } | |
477 | |
478 PortableDeviceWatcherWin::~PortableDeviceWatcherWin() { | |
479 } | |
480 | |
481 void PortableDeviceWatcherWin::Init() { | |
482 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
483 if (app_terminating_) | |
484 return; | |
485 | |
486 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); | |
487 base::SequencedWorkerPool::SequenceToken media_sequence_token = | |
488 pool->GetNamedSequenceToken(kMediaTaskRunnerName); | |
489 media_task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior( | |
490 media_sequence_token, base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); | |
491 EnumerateAttachedDevices(); | |
492 } | |
493 | |
494 void PortableDeviceWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) { | |
495 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
496 if (app_terminating_) | |
497 return; | |
498 | |
499 if (!IsPortableDeviceStructure(data)) | |
500 return; | |
501 | |
502 string16 device_id = GetPnpDeviceId(data); | |
503 if (event_type == DBT_DEVICEARRIVAL) | |
504 HandleDeviceAttachEvent(device_id); | |
505 else if (event_type == DBT_DEVICEREMOVECOMPLETE) | |
506 HandleDeviceDetachEvent(device_id); | |
507 } | |
508 | |
509 void PortableDeviceWatcherWin::EnumerateAttachedDevices() { | |
510 DCHECK(media_task_runner_.get()); | |
511 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
512 if (app_terminating_) | |
513 return; | |
514 DeviceDetailsVector* device_details_vector = new DeviceDetailsVector; | |
515 media_task_runner_->PostTaskAndReply( | |
516 FROM_HERE, | |
517 base::Bind(&EnumerateAttachedDevicesOnBlockingThread, | |
518 base::Unretained(device_details_vector)), | |
vandebo (ex-Chrome)
2012/10/26 20:32:10
nit: Unretained not needed
kmadhusu
2012/10/26 22:23:26
Removed.
| |
519 base::Bind(&PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices, | |
520 weak_ptr_factory_.GetWeakPtr(), | |
521 base::Owned(device_details_vector))); | |
522 } | |
523 | |
524 void PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices( | |
525 const DeviceDetailsVector* device_details_vector) { | |
526 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
527 DCHECK(device_details_vector); | |
528 for (DeviceDetailsVector::const_iterator it = device_details_vector->begin(); | |
529 it != device_details_vector->end(); ++it) { | |
530 OnDidHandleDeviceAttachEvent(&(*it)); | |
531 } | |
532 } | |
533 | |
534 void PortableDeviceWatcherWin::HandleDeviceAttachEvent( | |
535 const string16& pnp_device_id) { | |
536 DCHECK(media_task_runner_.get()); | |
537 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
538 if (app_terminating_) | |
539 return; | |
540 DeviceDetails* device_details = new DeviceDetails; | |
541 media_task_runner_->PostTaskAndReply( | |
542 FROM_HERE, | |
543 base::Bind(&HandleDeviceAttachedEventOnBlockingThread, pnp_device_id, | |
544 base::Unretained(device_details)), | |
545 base::Bind(&PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent, | |
546 weak_ptr_factory_.GetWeakPtr(), | |
547 base::Owned(device_details))); | |
548 } | |
549 | |
550 void PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent( | |
551 const DeviceDetails* device_details) { | |
552 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
553 DCHECK(device_details); | |
554 if (app_terminating_) | |
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 | |
562 base::SystemMonitor* system_monitor = base::SystemMonitor::Get(); | |
563 DCHECK(system_monitor); | |
564 for (StorageObjects::const_iterator storage_iter = storage_objects.begin(); | |
565 storage_iter != storage_objects.end(); ++storage_iter) { | |
566 const std::string& storage_id = storage_iter->object_persistent_id; | |
567 DCHECK(!ContainsKey(storage_map_, storage_id)); | |
568 | |
569 // Keep track of storage id and storage name to see how often we receive | |
570 // empty values. | |
571 MediaStorageUtil::RecordDeviceInfoHistogram(false, storage_id, name); | |
572 if (storage_id.empty() || name.empty()) | |
573 return; | |
574 | |
575 // Device can have several data partitions. Therefore, add the | |
576 // partition identifier to the storage name. E.g.: "Nexus 7 (s10001)" | |
577 string16 storage_name(name + L" (" + storage_iter->object_temporary_id + | |
578 L')'); | |
579 storage_map_[storage_id] = | |
580 base::SystemMonitor::RemovableStorageInfo(storage_id, storage_name, | |
581 location); | |
582 system_monitor->ProcessRemovableStorageAttached( | |
583 storage_id, storage_name, GetStoragePathFromStorageId(storage_id)); | |
584 } | |
585 device_map_[location] = storage_objects; | |
586 } | |
587 | |
588 void PortableDeviceWatcherWin::HandleDeviceDetachEvent( | |
589 const string16& pnp_device_id) { | |
590 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
591 MtpDeviceMap::iterator device_entry = device_map_.find(pnp_device_id); | |
592 if (device_entry == device_map_.end()) | |
593 return; | |
594 | |
595 base::SystemMonitor* system_monitor = base::SystemMonitor::Get(); | |
596 DCHECK(system_monitor); | |
597 | |
598 StorageObjects storage_objects = device_entry->second; | |
599 for (StorageObjects::const_iterator storage_iter = storage_objects.begin(); | |
600 storage_iter != storage_objects.end(); ++storage_iter) { | |
601 std::string storage_id = storage_iter->object_persistent_id; | |
602 MtpStorageMap::iterator storage_entry = storage_map_.find(storage_id); | |
603 DCHECK(storage_entry != storage_map_.end()); | |
604 system_monitor->ProcessRemovableStorageDetached( | |
605 storage_entry->second.device_id); | |
606 storage_map_.erase(storage_entry); | |
607 } | |
608 device_map_.erase(device_entry); | |
609 } | |
610 | |
611 void PortableDeviceWatcherWin::Observe( | |
612 int type, | |
613 const content::NotificationSource& source, | |
614 const content::NotificationDetails& details) { | |
615 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
616 DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type); | |
617 app_terminating_ = true; | |
618 } | |
619 | |
620 } // namespace chrome | |
OLD | NEW |