Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(384)

Side by Side Diff: chrome/browser/system_monitor/portable_device_watcher_win.cc

Issue 11088012: [Win, MediaGallery] Enumerate and handle mtp device attach/detach events. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed review comments Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 "content/public/browser/browser_thread.h"
21
22 namespace chrome {
23
24 namespace {
25
26 // Name of the client application that communicates with the mtp device.
27 const char16 kClientName[] = L"Chromium";
28
29 // Name of the sequenced task runner.
30 const char kMediaTaskRunnerName[] = "media-task-runner";
31
32 // Returns true if |data| represents a class of portable devices.
33 bool IsPortableDeviceStructure(LPARAM data) {
34 DEV_BROADCAST_HDR* broadcast_hdr =
35 reinterpret_cast<DEV_BROADCAST_HDR*>(data);
36 if (!broadcast_hdr ||
37 (broadcast_hdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)) {
38 return false;
39 }
40
41 GUID guidDevInterface = GUID_NULL;
42 if (FAILED(CLSIDFromString(kWPDDevInterfaceGUID, &guidDevInterface)))
43 return false;
44 DEV_BROADCAST_DEVICEINTERFACE* dev_interface =
45 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data);
46 return (IsEqualGUID(dev_interface->dbcc_classguid, guidDevInterface) != 0);
47 }
48
49 // Returns the portable device plug and play device ID string.
50 string16 GetPnpDeviceId(LPARAM data) {
51 DEV_BROADCAST_DEVICEINTERFACE* dev_interface =
52 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data);
53 if (!dev_interface)
54 return string16();
55 string16 device_id(dev_interface->dbcc_name);
56 DCHECK(IsStringASCII(device_id));
57 return StringToLowerASCII(device_id);
58 }
59
60 // Gets the friendly name of the device specified by the |pnp_device_id|. On
61 // success, returns true and fills in |name|.
62 bool GetFriendlyName(const string16& pnp_device_id,
63 IPortableDeviceManager* device_manager,
64 string16* name) {
65 DCHECK(device_manager);
66 DCHECK(name);
67 DWORD name_len = 0;
68 HRESULT hr = device_manager->GetDeviceFriendlyName(pnp_device_id.c_str(),
69 NULL, &name_len);
70 if (FAILED(hr))
71 return false;
72
73 hr = device_manager->GetDeviceFriendlyName(
74 pnp_device_id.c_str(), WriteInto(name, name_len), &name_len);
75 return (SUCCEEDED(hr) && !name->empty());
76 }
77
78 // Gets the manufacturer name of the device specified by the |pnp_device_id|.
79 // On success, returns true and fills in |name|.
80 bool GetManufacturerName(const string16& pnp_device_id,
81 IPortableDeviceManager* device_manager,
82 string16* name) {
83 DCHECK(device_manager);
84 DCHECK(name);
85 DWORD name_len = 0;
86 HRESULT hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(),
87 NULL, &name_len);
88 if (FAILED(hr))
89 return false;
90
91 hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(),
92 WriteInto(name, name_len),
93 &name_len);
94 return (SUCCEEDED(hr) && !name->empty());
95 }
96
97 // Gets the description of the device specified by the |pnp_device_id|. On
98 // success, returns true and fills in |description|.
99 bool GetDeviceDescription(const string16& pnp_device_id,
100 IPortableDeviceManager* device_manager,
101 string16* description) {
102 DCHECK(device_manager);
103 DCHECK(description);
104 DWORD desc_len = 0;
105 HRESULT hr = device_manager->GetDeviceDescription(pnp_device_id.c_str(), NULL,
106 &desc_len);
107 if (FAILED(hr))
108 return false;
109
110 hr = device_manager->GetDeviceDescription(pnp_device_id.c_str(),
111 WriteInto(description, desc_len),
112 &desc_len);
113 return (SUCCEEDED(hr) && !description->empty());
114 }
115
116 // On success, returns true and updates |client_info| with a reference to an
117 // IPortableDeviceValues interface that holds information about the
118 // application that communicates with the device.
119 bool GetClientInformation(
120 base::win::ScopedComPtr<IPortableDeviceValues>* client_info) {
121 HRESULT hr = client_info->CreateInstance(__uuidof(PortableDeviceValues),
122 NULL, CLSCTX_INPROC_SERVER);
123 if (FAILED(hr)) {
124 DPLOG(ERROR) << "Failed to create an instance of IPortableDeviceValues";
125 return false;
126 }
127
128 // Attempt to set client details.
129 (*client_info)->SetStringValue(WPD_CLIENT_NAME, kClientName);
130 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, 0);
131 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, 0);
132 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_REVISION, 0);
133 (*client_info)->SetUnsignedIntegerValue(
134 WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE, SECURITY_IMPERSONATION);
135 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS,
136 GENERIC_READ);
137 return true;
138 }
139
140 // Opens the device for communication. |pnp_device_id| specifies the plug and
141 // play device ID string. On success, returns true and updates |device| with a
142 // reference to the portable device interface.
143 bool SetUp(const string16& pnp_device_id,
144 base::win::ScopedComPtr<IPortableDevice>* device) {
145 base::win::ScopedComPtr<IPortableDeviceValues> client_info;
146 if (!GetClientInformation(&client_info))
147 return false;
148
149 HRESULT hr = device->CreateInstance(__uuidof(PortableDevice), NULL,
150 CLSCTX_INPROC_SERVER);
151 if (FAILED(hr)) {
152 DPLOG(ERROR) << "Failed to create an instance of IPortableDevice";
153 return false;
154 }
155
156 hr = (*device)->Open(pnp_device_id.c_str(), client_info.get());
157 if (SUCCEEDED(hr))
158 return true;
159
160 if (hr == E_ACCESSDENIED)
161 DPLOG(ERROR) << "Access denied to open the device";
162 return false;
163 }
164
165 // Returns the unique id property key of the object specified by the
166 // |object_id|.
167 REFPROPERTYKEY GetUniqueIdPropertyKey(const string16& object_id) {
168 return (object_id == WPD_DEVICE_OBJECT_ID) ?
169 WPD_DEVICE_SERIAL_NUMBER : WPD_OBJECT_PERSISTENT_UNIQUE_ID;
170 }
171
172 // On success, returns true and populates |properties_to_read| with the
173 // property key of the object specified by the |object_id|.
174 bool PopulatePropertyKeyCollection(
175 const string16& object_id,
176 base::win::ScopedComPtr<IPortableDeviceKeyCollection>* properties_to_read) {
177 HRESULT hr = properties_to_read->CreateInstance(
178 __uuidof(PortableDeviceKeyCollection), NULL, CLSCTX_INPROC_SERVER);
179 if (FAILED(hr)) {
180 DPLOG(ERROR) << "Failed to create IPortableDeviceKeyCollection instance";
181 return false;
182 }
183 REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id);
184 hr = (*properties_to_read)->Add(key);
185 return SUCCEEDED(hr);
186 }
187
188 // Wrapper function to get content property string value.
189 bool GetStringPropertyValue(IPortableDeviceValues* properties_values,
190 REFPROPERTYKEY key,
191 string16* value) {
192 DCHECK(properties_values);
193 DCHECK(value);
194 base::win::ScopedCoMem<char16> buffer;
195 HRESULT hr = properties_values->GetStringValue(key, &buffer);
196 if (FAILED(hr))
197 return false;
198 *value = static_cast<const char16*>(buffer);
199 return true;
200 }
201
202 // Constructs a unique identifier for the object specified by the |object_id|.
203 // On success, returns true and fills in |unique_id|.
204 bool GetObjectUniqueId(IPortableDevice* device,
205 const string16& object_id,
206 string16* unique_id) {
207 DCHECK(device);
208 DCHECK(unique_id);
209 base::win::ScopedComPtr<IPortableDeviceContent> content;
210 HRESULT hr = device->Content(content.Receive());
211 if (FAILED(hr)) {
212 DPLOG(ERROR) << "Failed to get IPortableDeviceContent interface";
213 return false;
214 }
215
216 base::win::ScopedComPtr<IPortableDeviceProperties> properties;
217 hr = content->Properties(properties.Receive());
218 if (FAILED(hr)) {
219 DPLOG(ERROR) << "Failed to get IPortableDeviceProperties interface";
220 return false;
221 }
222
223 base::win::ScopedComPtr<IPortableDeviceKeyCollection> properties_to_read;
224 if (!PopulatePropertyKeyCollection(object_id, &properties_to_read))
225 return false;
226
227 base::win::ScopedComPtr<IPortableDeviceValues> properties_values;
228 if (FAILED(properties->GetValues(object_id.c_str(),
229 properties_to_read.get(),
230 properties_values.Receive()))) {
231 return false;
232 }
233
234 REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id);
235 return GetStringPropertyValue(properties_values.get(), key, unique_id);
236 }
237
238 // Constructs the device storage unique identifier using |device_serial_num| and
239 // |storage_id|. On success, returns true and fills in |device_storage_id|.
240 bool ConstructDeviceStorageUniqueId(const string16& device_serial_num,
241 const string16& storage_id,
242 std::string* device_storage_id) {
243 if (device_serial_num.empty() && storage_id.empty())
244 return false;
245
246 DCHECK(device_storage_id);
247 string16 unique_id(storage_id + L':' + device_serial_num);
Peter Kasting 2012/10/29 21:57:19 Nit: I suggest inlining this below to avoid the te
kmadhusu 2012/10/30 01:29:24 Done.
248
Peter Kasting 2012/10/29 21:57:19 Nit: Blank line unnecessary
kmadhusu 2012/10/30 01:29:24 Done.
249 *device_storage_id = MediaStorageUtil::MakeDeviceId(
250 MediaStorageUtil::MTP_OR_PTP, UTF16ToUTF8(unique_id));
251 return true;
252 }
253
254 // Gets a list of removable storage object identifiers present in |device|.
255 // On success, returns true and fills in |storage_object_ids|.
256 bool GetRemovableStorageObjectIds(
257 IPortableDevice* device,
258 PortableDeviceWatcherWin::StorageObjectIDs* storage_object_ids) {
259 DCHECK(device);
260 DCHECK(storage_object_ids);
261 base::win::ScopedComPtr<IPortableDeviceCapabilities> capabilities;
262 HRESULT hr = device->Capabilities(capabilities.Receive());
263 if (FAILED(hr)) {
264 DPLOG(ERROR) << "Failed to get IPortableDeviceCapabilities interface";
265 return false;
266 }
267
268 base::win::ScopedComPtr<IPortableDevicePropVariantCollection> storage_ids;
269 hr = capabilities->GetFunctionalObjects(WPD_FUNCTIONAL_CATEGORY_STORAGE,
270 storage_ids.Receive());
271 if (FAILED(hr)) {
272 DPLOG(ERROR) << "Failed to get IPortableDevicePropVariantCollection";
273 return false;
274 }
275
276 DWORD num_storage_obj_ids = 0;
277 hr = storage_ids->GetCount(&num_storage_obj_ids);
278 if (FAILED(hr))
279 return false;
280
281 for (DWORD index = 0; index < num_storage_obj_ids; ++index) {
282 PROPVARIANT object_id = {0};
283 PropVariantInit(&object_id);
284 hr = storage_ids->GetAt(index, &object_id);
285 if (SUCCEEDED(hr) && (object_id.pwszVal != NULL) &&
286 (object_id.vt == VT_LPWSTR)) {
287 storage_object_ids->push_back(object_id.pwszVal);
288 }
289 PropVariantClear(&object_id);
290 }
291 return true;
292 }
293
294 // Returns true if the portable device is mounted on a volume. |device_name|
295 // specifies the name of the device.
296 bool IsVolumeMountedPortableDevice(const string16& device_name) {
297 // If the device is a volume mounted device, |device_name| will be
298 // the volume name.
299 return ((device_name.length() >= 2) && (device_name[1] == L':') &&
300 (((device_name[0] >= L'A') && (device_name[0] <= L'Z')) ||
301 ((device_name[0] >= L'a') && (device_name[0] <= L'z'))));
302 }
303
304 // Returns the name of the device specified by |pnp_device_id|.
305 // Since this task may take a longer time to complete, this function is
306 // accessed via the media task runner.
307 string16 GetDeviceNameOnBlockingThread(
308 IPortableDeviceManager* portable_device_manager,
309 const string16& pnp_device_id) {
310 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
311 DCHECK(portable_device_manager);
312 string16 name;
313 GetFriendlyName(pnp_device_id, portable_device_manager, &name) ||
314 GetDeviceDescription(pnp_device_id, portable_device_manager, &name) ||
315 GetManufacturerName(pnp_device_id, portable_device_manager, &name);
316 return name;
317 }
318
319 // Access the device and gets the device storage details. Since this task may
320 // take a longer time to complete, this function is accessed via the media task
321 // runner. On success, returns true and populates |storage_objects| with device
322 // storage details.
323 bool GetDeviceStorageObjectsOnBlockingThread(
324 const string16& pnp_device_id,
325 PortableDeviceWatcherWin::StorageObjects* storage_objects) {
326 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
327 DCHECK(storage_objects);
328 base::win::ScopedComPtr<IPortableDevice> device;
329 if (!SetUp(pnp_device_id, &device))
330 return false;
331
332 string16 device_serial_num;
333 if (!GetObjectUniqueId(device.get(), WPD_DEVICE_OBJECT_ID,
334 &device_serial_num)) {
335 return false;
336 }
337
338 PortableDeviceWatcherWin::StorageObjectIDs storage_obj_ids;
339 if (!GetRemovableStorageObjectIds(device.get(), &storage_obj_ids))
340 return false;
341
342 for (PortableDeviceWatcherWin::StorageObjectIDs::const_iterator id_iter =
343 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 storage_objects->push_back(PortableDeviceWatcherWin::DeviceStorageObject(
353 *id_iter, device_storage_id));
354 }
355 }
356 return true;
357 }
358
359 // Accesses the device and gets the device details (name, storage info, etc).
360 // Since this task may take a longer time to complete, this function is
361 // accessed via the media task runner. On success returns true and fills in
362 // |device_details|. |pnp_device_id| specifies the plug and play device ID
363 // string.
364 bool GetDeviceInfoOnBlockingThread(
365 IPortableDeviceManager* portable_device_manager,
366 const string16& pnp_device_id,
367 PortableDeviceWatcherWin::DeviceDetails* device_details) {
368 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
369 DCHECK(portable_device_manager);
370 DCHECK(device_details);
371 DCHECK(!pnp_device_id.empty());
372
Peter Kasting 2012/10/29 21:57:19 Nit: No blank line after precondition DCHECKs (sin
kmadhusu 2012/10/30 01:29:24 Done.
373 device_details->name = GetDeviceNameOnBlockingThread(portable_device_manager,
374 pnp_device_id);
375 if (IsVolumeMountedPortableDevice(device_details->name))
376 return false;
377
378 device_details->location = pnp_device_id;
379 PortableDeviceWatcherWin::StorageObjects storage_objects;
380 return GetDeviceStorageObjectsOnBlockingThread(
381 pnp_device_id, &device_details->storage_objects);
382 }
383
384 // Wrapper function to get an instance of portable device manager. This function
385 // may take a long time to complete. Therefore, access this function via a media
386 // task runner. On success, returns true and fills in |portable_device_mgr|.
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. This function accesses the device
402 // to get the device details. Since this task may take a longer time to
403 // complete, this function is accessed via the media task runner. On success,
404 // fills in |devices| with the attached portable device details. On failure,
405 // |result| will be assigned false.
406 void EnumerateAttachedDevicesOnBlockingThread(
407 PortableDeviceWatcherWin::Devices* devices,
408 bool* result) {
409 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
410 DCHECK(devices);
411 DCHECK(result);
412 *result = false;
413 base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr;
414 if (!GetPortableDeviceManager(&portable_device_mgr))
415 return;
416
417 // Get the total number of devices found on the system.
418 DWORD pnp_device_count = 0;
419 HRESULT hr = portable_device_mgr->GetDevices(NULL, &pnp_device_count);
420 if (FAILED(hr))
421 return;
422
423 scoped_array<char16*> pnp_device_ids(new char16*[pnp_device_count]);
424 ZeroMemory(pnp_device_ids.get(), pnp_device_count);
425 hr = portable_device_mgr->GetDevices(pnp_device_ids.get(), &pnp_device_count);
426 if (FAILED(hr))
427 return;
428
429 for (DWORD index = 0; index < pnp_device_count; ++index) {
430 PortableDeviceWatcherWin::DeviceDetails device_details;
431 if (!GetDeviceInfoOnBlockingThread(portable_device_mgr,
Peter Kasting 2012/10/29 21:57:19 Nit: Or just: if (GetDeviceInfoOnBlockingThre
kmadhusu 2012/10/30 01:29:24 Done.
432 pnp_device_ids[index],
433 &device_details)) {
434 continue;
435 }
436 devices->push_back(device_details);
437 }
438 *result = !devices->empty();
439 }
440
441 // Handles the device attach event message on a media task runner. This function
442 // communicates with the device to get the storage details. This task may take a
443 // longer time. Therefore, access this function on a media task runner.
444 // |pnp_device_id| specifies the attached plug and play device ID string. On
445 // success, populates |device_details| with device information. On failure,
446 // |result| will be assigned false.
447 void HandleDeviceAttachedEventOnBlockingThread(
448 const string16& pnp_device_id,
449 PortableDeviceWatcherWin::DeviceDetails* device_details,
450 bool* result) {
Peter Kasting 2012/10/29 21:57:19 You should be able to avoid this outparam and retu
kmadhusu 2012/10/30 01:29:24 Done.
451 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
452 DCHECK(device_details);
453 DCHECK(result);
454 base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr;
455 if (!GetPortableDeviceManager(&portable_device_mgr)) {
456 *result = false;
457 return;
458 }
459 // Sometimes, portable device manager doesn't have the new device details.
460 // Refresh the manager device list to update its details.
461 portable_device_mgr->RefreshDeviceList();
462 *result = GetDeviceInfoOnBlockingThread(portable_device_mgr, pnp_device_id,
463 device_details);
464 }
465
466 // Constructs and returns a storage path from storage unique identifier.
467 string16 GetStoragePathFromStorageId(const std::string& storage_unique_id) {
468 // Construct a dummy device path using the storage name. This is only used
469 // for registering the device media file system.
470 DCHECK(!storage_unique_id.empty());
471 return UTF8ToUTF16("\\\\" + storage_unique_id);
472 }
473
474 } // namespace
475
Peter Kasting 2012/10/29 21:57:19 Nit: Consider adding one more blank line here to m
kmadhusu 2012/10/30 01:29:24 Done.
476 // PortableDeviceWatcherWin ---------------------------------------------------
477
478 PortableDeviceWatcherWin::DeviceStorageObject::DeviceStorageObject(
479 const string16& temporary_id,
480 const std::string& persistent_id)
481 : object_temporary_id(temporary_id),
482 object_persistent_id(persistent_id) {
483 }
484
485 PortableDeviceWatcherWin::PortableDeviceWatcherWin()
486 : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
487 }
488
489 PortableDeviceWatcherWin::~PortableDeviceWatcherWin() {
490 }
491
492 void PortableDeviceWatcherWin::Init() {
493 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
494
495 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
496 base::SequencedWorkerPool::SequenceToken media_sequence_token =
497 pool->GetNamedSequenceToken(kMediaTaskRunnerName);
Peter Kasting 2012/10/29 21:57:19 Nit: The declaration here is so verbose I'd just i
kmadhusu 2012/10/30 01:29:24 Done.
498 media_task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior(
499 media_sequence_token, base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
500 EnumerateAttachedDevices();
501 }
502
503 void PortableDeviceWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) {
504 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
505
506 if (!IsPortableDeviceStructure(data))
507 return;
508
509 string16 device_id = GetPnpDeviceId(data);
510 if (event_type == DBT_DEVICEARRIVAL)
511 HandleDeviceAttachEvent(device_id);
512 else if (event_type == DBT_DEVICEREMOVECOMPLETE)
513 HandleDeviceDetachEvent(device_id);
514 }
515
516 void PortableDeviceWatcherWin::EnumerateAttachedDevices() {
517 DCHECK(media_task_runner_.get());
518 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
519
520 Devices* devices = new Devices;
521 bool* result = new bool(false);
522 media_task_runner_->PostTaskAndReply(
523 FROM_HERE,
524 base::Bind(&EnumerateAttachedDevicesOnBlockingThread, devices, result),
525 base::Bind(&PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices,
526 weak_ptr_factory_.GetWeakPtr(), base::Owned(devices),
527 base::Owned(result)));
528 }
529
530 void PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices(
531 const Devices* devices, const bool* result) {
532 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
533 DCHECK(devices);
534 DCHECK(result);
535 if (!(*result))
536 return;
537
538 for (Devices::const_iterator device_iter = devices->begin();
539 device_iter != devices->end(); ++device_iter) {
540 OnDidHandleDeviceAttachEvent(&(*device_iter), result);
541 }
542 }
543
544 void PortableDeviceWatcherWin::HandleDeviceAttachEvent(
545 const string16& pnp_device_id) {
546 DCHECK(media_task_runner_.get());
547 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
548
549 DeviceDetails* device_details = new DeviceDetails;
550 bool* result = new bool(false);
551 media_task_runner_->PostTaskAndReply(
552 FROM_HERE,
553 base::Bind(&HandleDeviceAttachedEventOnBlockingThread, pnp_device_id,
554 device_details, result),
555 base::Bind(&PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent,
556 weak_ptr_factory_.GetWeakPtr(), base::Owned(device_details),
557 base::Owned(result)));
558 }
559
560 void PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent(
561 const DeviceDetails* device_details, const bool* result) {
562 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
563 DCHECK(device_details);
564 DCHECK(result);
565
566 if (!(*result))
567 return;
568
569 const StorageObjects& storage_objects = device_details->storage_objects;
570 const string16& name = device_details->name;
571 const string16& location = device_details->location;
572 DCHECK(!ContainsKey(device_map_, location));
573
574 base::SystemMonitor* system_monitor = base::SystemMonitor::Get();
575 DCHECK(system_monitor);
576 for (StorageObjects::const_iterator storage_iter = storage_objects.begin();
577 storage_iter != storage_objects.end(); ++storage_iter) {
578 const std::string& storage_id = storage_iter->object_persistent_id;
579 DCHECK(!ContainsKey(storage_map_, storage_id));
580
581 // Keep track of storage id and storage name to see how often we receive
582 // empty values.
583 MediaStorageUtil::RecordDeviceInfoHistogram(false, storage_id, name);
584 if (storage_id.empty() || name.empty())
585 return;
586
587 // Device can have several data partitions. Therefore, add the
588 // partition identifier to the storage name. E.g.: "Nexus 7 (s10001)"
589 string16 storage_name(name + L" (" + storage_iter->object_temporary_id +
590 L')');
591 storage_map_[storage_id] =
592 base::SystemMonitor::RemovableStorageInfo(storage_id, storage_name,
593 location);
594 system_monitor->ProcessRemovableStorageAttached(
595 storage_id, storage_name, GetStoragePathFromStorageId(storage_id));
596 }
597 device_map_[location] = storage_objects;
598 }
599
600 void PortableDeviceWatcherWin::HandleDeviceDetachEvent(
601 const string16& pnp_device_id) {
602 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
603 MtpDeviceMap::iterator device_iter = device_map_.find(pnp_device_id);
604 if (device_iter == device_map_.end())
605 return;
606
607 base::SystemMonitor* system_monitor = base::SystemMonitor::Get();
608 DCHECK(system_monitor);
609
610 const StorageObjects& storage_objects = device_iter->second;
611 for (StorageObjects::const_iterator storage_object_iter =
612 storage_objects.begin();
613 storage_object_iter != storage_objects.end(); ++storage_object_iter) {
614 std::string storage_id = storage_object_iter->object_persistent_id;
615 MtpStorageMap::iterator storage_iter = storage_map_.find(storage_id);
616 DCHECK(storage_iter != storage_map_.end());
617 system_monitor->ProcessRemovableStorageDetached(
618 storage_iter->second.device_id);
619 storage_map_.erase(storage_iter);
620 }
621 device_map_.erase(device_iter);
622 }
623
624 } // namespace chrome
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698