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

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 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 "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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698