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

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: Removed StringPrintf() Created 8 years, 2 months 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 DWORD name_len = 0;
67 HRESULT hr = device_manager->GetDeviceFriendlyName(pnp_device_id.c_str(),
68 NULL, &name_len);
69 if (FAILED(hr))
70 return false;
71
72 string16 friendly_name;
73 hr = device_manager->GetDeviceFriendlyName(
74 pnp_device_id.c_str(), WriteInto(&friendly_name, name_len), &name_len);
75 if (FAILED(hr))
76 return false;
77
78 if (name)
79 *name = friendly_name;
80 return !friendly_name.empty();
81 }
82
83 // Gets the manufacturer name of the device specified by the |pnp_device_id|.
84 // On success, returns true and fills in |name|.
85 bool GetManufacturerName(const string16& pnp_device_id,
86 IPortableDeviceManager* device_manager,
87 string16* name) {
88 DWORD name_len = 0;
89 HRESULT hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(),
90 NULL, &name_len);
91 if (FAILED(hr))
92 return false;
93
94 string16 manufacturer_name;
95 hr = device_manager->GetDeviceManufacturer(
96 pnp_device_id.c_str(), WriteInto(&manufacturer_name, name_len),
97 &name_len);
98 if (FAILED(hr))
99 return false;
100
101 if (name)
102 *name = manufacturer_name;
103 return !manufacturer_name.empty();
104 }
105
106 // Gets the description of the device specified by the |pnp_device_id|. On
107 // success, returns true and fills in |name|.
108 bool GetDeviceDescription(const string16& pnp_device_id,
109 IPortableDeviceManager* device_manager,
110 string16* name) {
111 DWORD desc_len = 0;
112 HRESULT hr = device_manager->GetDeviceDescription(pnp_device_id.c_str(), NULL,
113 &desc_len);
114 if (FAILED(hr))
115 return false;
116
117 string16 description;
118 hr = device_manager->GetDeviceDescription(pnp_device_id.c_str(),
119 WriteInto(&description, desc_len),
120 &desc_len);
121 if (FAILED(hr))
122 return false;
123 if (name)
124 *name = description;
125 return !description.empty();
126 }
127
128 // On success, returns true and updates |client_info| with a reference to an
129 // IPortableDeviceValues interface that holds information about the
130 // application that communicates with the device.
131 bool GetClientInformation(
132 base::win::ScopedComPtr<IPortableDeviceValues>* client_info) {
133 HRESULT hr = client_info->CreateInstance(__uuidof(PortableDeviceValues),
134 NULL, CLSCTX_INPROC_SERVER);
135 if (FAILED(hr)) {
136 DPLOG(ERROR) << "Failed to create an instance of IPortableDeviceValues";
137 return false;
138 }
139
140 // Attempt to set client details.
141 (*client_info)->SetStringValue(WPD_CLIENT_NAME, kClientName);
142
143 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, 0);
144
145 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, 0);
146
147 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_REVISION, 0);
148
149 (*client_info)->SetUnsignedIntegerValue(
150 WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE, SECURITY_IMPERSONATION);
151
152 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS,
153 GENERIC_READ);
154 return true;
155 }
156
157 // Opens the device for communication. |pnp_device_id| specifies the plug and
158 // play device ID string. On success, returns true and updates |device| with a
159 // reference to the portable device interface.
160 bool Setup(const string16& pnp_device_id,
vandebo (ex-Chrome) 2012/10/25 19:24:47 nit: SetUp
kmadhusu 2012/10/26 02:01:24 Done.
161 base::win::ScopedComPtr<IPortableDevice>* device) {
162 base::win::ScopedComPtr<IPortableDeviceValues> client_info;
163 if (!GetClientInformation(&client_info))
164 return false;
165
166 HRESULT hr = device->CreateInstance(__uuidof(PortableDevice), NULL,
167 CLSCTX_INPROC_SERVER);
168 if (FAILED(hr)) {
169 DPLOG(ERROR) << "Failed to create an instance of IPortableDevice";
170 return false;
171 }
172
173 hr = (*device)->Open(pnp_device_id.c_str(), client_info.get());
174 if (FAILED(hr)) {
175 if (hr == E_ACCESSDENIED)
176 DPLOG(ERROR) << "Access denied to open the device";
177 return false;
178 }
179 return true;
180 }
181
182 // Returns true if the object specified by the |object_id| is the device root
183 // object.
184 bool IsDeviceObjectId(const string16& object_id) {
vandebo (ex-Chrome) 2012/10/25 19:24:47 Used once (in a simple function), inline.
kmadhusu 2012/10/26 02:01:24 Done.
185 return (object_id == WPD_DEVICE_OBJECT_ID);
186 }
187
188 // Returns the unique id property key of the object specified by the
189 // |object_id|.
190 REFPROPERTYKEY GetUniqueIdPropertyKey(const string16& object_id) {
191 return IsDeviceObjectId(object_id) ?
192 WPD_DEVICE_SERIAL_NUMBER : WPD_OBJECT_PERSISTENT_UNIQUE_ID;
193 }
194
195 // On success, returns true and populates |properties_to_read| with the
196 // property key of the object specified by the |object_id|.
197 bool PopulatePropertyKeyCollection(
198 const string16& object_id,
199 base::win::ScopedComPtr<IPortableDeviceKeyCollection>* properties_to_read) {
200 HRESULT hr = properties_to_read->CreateInstance(
201 __uuidof(PortableDeviceKeyCollection), NULL, CLSCTX_INPROC_SERVER);
202 if (FAILED(hr)) {
203 DPLOG(ERROR) << "Failed to create IPortableDeviceKeyCollection instance";
204 return false;
205 }
206 REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id);
207 hr = (*properties_to_read)->Add(key);
208 return SUCCEEDED(hr);
209 }
210
211 // Wrapper function to get content property string value.
212 bool GetStringPropertyValue(IPortableDeviceValues* properties_values,
213 REFPROPERTYKEY key,
214 string16* value) {
215 base::win::ScopedCoMem<char16> buffer;
216 HRESULT hr = properties_values->GetStringValue(key, &buffer);
217 if (FAILED(hr))
218 return false;
219
220 if (value)
221 *value = string16(buffer);
222 return true;
223 }
224
225 // Constructs a unique identifier for the object specified by the |object_id|.
226 // On success, returns true and fills in |unique_id|.
227 bool GetObjectUniqueId(IPortableDevice* device,
228 const string16& object_id,
229 string16* unique_id) {
230 base::win::ScopedComPtr<IPortableDeviceContent> content;
231 HRESULT hr = device->Content(content.Receive());
232 if (FAILED(hr)) {
233 DPLOG(ERROR) << "Failed to get IPortableDeviceContent interface";
234 return false;
235 }
236
237 base::win::ScopedComPtr<IPortableDeviceProperties> properties;
238 hr = content->Properties(properties.Receive());
239 if (FAILED(hr)) {
240 DPLOG(ERROR) << "Failed to get IPortableDeviceProperties interface";
241 return false;
242 }
243
244 base::win::ScopedComPtr<IPortableDeviceKeyCollection> properties_to_read;
245 if (!PopulatePropertyKeyCollection(object_id, &properties_to_read))
246 return false;
247
248 base::win::ScopedComPtr<IPortableDeviceValues> properties_values;
249 if (FAILED(properties->GetValues(object_id.c_str(),
250 properties_to_read.get(),
251 properties_values.Receive()))) {
252 return false;
253 }
254
255 REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id);
256 return GetStringPropertyValue(properties_values.get(), key, unique_id);
257 }
258
259 // Constructs the device storage unique identifier using |device_serial_num| and
260 // |storage_id|. On success, returns true and fills in |device_storage_id|.
261 bool GetDeviceStorageUniqueId(const string16& device_serial_num,
vandebo (ex-Chrome) 2012/10/25 19:24:47 nit: MakeDeviceStorageDeviceId or ConstructDeviceS
kmadhusu 2012/10/26 02:01:24 Renamed GetDeviceStorageUniqueId => ConstructDevic
262 const string16& storage_id,
263 std::string* device_storage_id) {
264 if (device_serial_num.empty() && storage_id.empty())
265 return false;
266
267 // |unique_id| format: StorageSerial:|storage_id|:|device_serial_num|
Peter Kasting 2012/10/25 05:23:24 Nit: Remove this comment, it just duplicates the c
kmadhusu 2012/10/26 02:01:24 Done.
268 string16 unique_id(chrome::kMtpDeviceStorageIdPrefix + storage_id +
269 UTF8ToUTF16(chrome::kNonSpaceDelim) + device_serial_num);
Peter Kasting 2012/10/25 05:23:24 First, use ASCIIToUTF16() when you know the input
kmadhusu 2012/10/26 02:01:24 Removed UTF..() function and used L':' directly.
270
271 // |device_storage_id| format: mtp:|unique_id|
272 *device_storage_id = MediaStorageUtil::MakeDeviceId(
273 MediaStorageUtil::MTP_OR_PTP, UTF16ToUTF8(unique_id));
274 return true;
275 }
276
277 // Gets a list of removable storage object identifiers present in |device|.
278 // On success, returns true and fills in |storage_ids|.
279 bool GetRemovableStorageObjectIds(IPortableDevice* device,
280 std::vector<string16>* storage_ids) {
281 base::win::ScopedComPtr<IPortableDeviceCapabilities> capabilities;
282 HRESULT hr = device->Capabilities(capabilities.Receive());
283 if (FAILED(hr)) {
284 DPLOG(ERROR) << "Failed to get IPortableDeviceCapabilities interface";
285 return false;
286 }
287
288 base::win::ScopedComPtr<IPortableDevicePropVariantCollection> storage_obj_ids;
289 hr = capabilities->GetFunctionalObjects(WPD_FUNCTIONAL_CATEGORY_STORAGE,
290 storage_obj_ids.Receive());
291 if (FAILED(hr)) {
292 DPLOG(ERROR) << "Failed to get IPortableDevicePropVariantCollection";
293 return false;
294 }
295
296 DWORD num_storage_obj_ids = 0;
297 hr = storage_obj_ids->GetCount(&num_storage_obj_ids);
298 if (FAILED(hr))
299 return false;
300
301 for (DWORD index = 0; index < num_storage_obj_ids; ++index) {
302 PROPVARIANT object_id = {0};
303 PropVariantInit(&object_id);
304 hr = storage_obj_ids->GetAt(index, &object_id);
305 if (SUCCEEDED(hr) && (object_id.pwszVal != NULL) &&
306 (object_id.vt == VT_LPWSTR)) {
307 storage_ids->push_back(object_id.pwszVal);
308 }
309 PropVariantClear(&object_id);
310 }
311 return true;
312 }
313
314 // Returns true if the portable device is mounted on a volume. |device_name|
315 // specifies the name of the device.
316 bool IsVolumeMountedPortableDevice(const string16& device_name) {
317 // If the device is a volume mounted device, |device_name| will be
318 // the volume name.
319 return (device_name.length() >= 2) && (device_name[1] == L':') &&
320 ((device_name[0] >= L'A') && (device_name[0] <= L'Z') ||
321 ((device_name[0] >= L'a') && (device_name[0] <= L'z')));
322 }
323
324 // Returns the name of the device specified by |pnp_device_id|.
325 // Accessed on the blocking thread.
326 string16 GetDeviceNameOnBlockingThread(
327 IPortableDeviceManager* portable_device_manager,
328 const string16& pnp_device_id) {
329 DCHECK(content::BrowserThread::
330 GetBlockingPool()->RunsTasksOnCurrentThread());
331 string16 name;
332 if (!(GetFriendlyName(pnp_device_id, portable_device_manager, &name) ||
333 GetDeviceDescription(pnp_device_id, portable_device_manager,
334 &name) ||
335 GetManufacturerName(pnp_device_id, portable_device_manager,
336 &name))) {
337 return string16();
338 }
339 return name;
340 }
341
342 // Gets the device storage details on the blocking thread. On success, returns
343 // true and populates |storage_info_list| with device storage details.
344 bool GetDeviceStorageInfoListOnBlockingThread(
345 const string16& pnp_device_id,
346 std::vector<PortableDeviceWatcherWin::DeviceStorageInfo>*
347 storage_info_list) {
348 DCHECK(content::BrowserThread::
349 GetBlockingPool()->RunsTasksOnCurrentThread());
350 base::win::ScopedComPtr<IPortableDevice> device;
351 if (!Setup(pnp_device_id, &device))
352 return false;
353
354 std::vector<string16> storage_obj_ids;
vandebo (ex-Chrome) 2012/10/25 19:24:47 Unless there's a reason to do this before getting
kmadhusu 2012/10/26 02:01:24 Done.
355 if (!GetRemovableStorageObjectIds(device.get(), &storage_obj_ids))
356 return false;
357
358 // Get the device serial number (E.g.: 4889033500677371).
359 string16 device_serial_num;
360 if (!GetObjectUniqueId(device.get(), WPD_DEVICE_OBJECT_ID,
361 &device_serial_num)) {
362 return false;
363 }
364
365 for (size_t index = 0; index < storage_obj_ids.size(); ++index) {
366 // Get the storage object persistent id (E.g.: SID-{10001,D,31080448}).
367 string16 storage_unique_id;
368 if (!GetObjectUniqueId(device.get(), storage_obj_ids[index],
vandebo (ex-Chrome) 2012/10/25 19:24:47 If you like, you could do: if (!GetObjectUniqueId
kmadhusu 2012/10/26 02:01:24 I would like to leave it as it is.
369 &storage_unique_id)) {
370 continue;
371 }
372 std::string device_storage_id;
373 if (GetDeviceStorageUniqueId(device_serial_num, storage_unique_id,
374 &device_storage_id)) {
375 PortableDeviceWatcherWin::DeviceStorageInfo storage_info;
376 storage_info.storage_object_id = storage_obj_ids[index];
377 storage_info.unique_id = device_storage_id;
378 storage_info_list->push_back(storage_info);
379 }
380 }
381 return true;
382 }
383
384 // Gets the device details (name, number of storages, etc.,) on the blocking
385 // thread. |pnp_device_id| specifies the plug and play device ID string.
386 PortableDeviceWatcherWin::DeviceDetails GetDeviceInfoOnBlockingThread(
387 IPortableDeviceManager* portable_device_manager,
388 const string16& pnp_device_id) {
389 DCHECK(content::BrowserThread::
390 GetBlockingPool()->RunsTasksOnCurrentThread());
391 DCHECK(!pnp_device_id.empty());
392 PortableDeviceWatcherWin::DeviceDetails device_details;
393 string16 device_name = GetDeviceNameOnBlockingThread(portable_device_manager,
394 pnp_device_id);
395 std::vector<PortableDeviceWatcherWin::DeviceStorageInfo> storage_info_list;
396 if (!GetDeviceStorageInfoListOnBlockingThread(pnp_device_id,
397 &storage_info_list)) {
398 return device_details;
399 }
400
401 if (IsVolumeMountedPortableDevice(device_name))
vandebo (ex-Chrome) 2012/10/25 19:24:47 Move above GetDeviceStorageInfoListOnBlockingThrea
kmadhusu 2012/10/26 02:01:24 Done.
402 return device_details;
403
404 device_details.storage_info_list = storage_info_list;
405 device_details.name = device_name;
406 device_details.location = pnp_device_id;
407 return device_details;
408 }
409
410 // Enumerates and returns a list of attached mtp device details on a
411 // blocking thread.
412 std::vector<PortableDeviceWatcherWin::DeviceDetails>
413 EnumerateAttachedStoragesOnBlockingThread() {
414 DCHECK(content::BrowserThread::
415 GetBlockingPool()->RunsTasksOnCurrentThread());
416 std::vector<PortableDeviceWatcherWin::DeviceDetails> device_info_list;
417 base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr;
418 HRESULT hr = portable_device_mgr.CreateInstance(
419 __uuidof(PortableDeviceManager), NULL, CLSCTX_INPROC_SERVER);
420 if (FAILED(hr)) {
421 // Either there is no portable device support (it must be a XP with
422 // Windows Media Player Version < 10) or the thread does not have COM
423 // initialized.
424 DCHECK_NE(CO_E_NOTINITIALIZED, hr);
425 return device_info_list;
426 }
427
428 // Get the total number of devices found on the system.
429 DWORD pnp_device_count = 0;
430 hr = portable_device_mgr->GetDevices(NULL, &pnp_device_count);
431 if (FAILED(hr))
432 return device_info_list;
433
434 scoped_array<LPWSTR> pnp_device_ids(new LPWSTR[pnp_device_count]);
435 ZeroMemory(pnp_device_ids.get(), pnp_device_count);
436 hr = portable_device_mgr->GetDevices(pnp_device_ids.get(), &pnp_device_count);
437 if (FAILED(hr))
438 return device_info_list;
439
440 for (DWORD index = 0; index < pnp_device_count; ++index) {
441 device_info_list.push_back(GetDeviceInfoOnBlockingThread(
442 portable_device_mgr, pnp_device_ids[index]));
443 }
444 return device_info_list;
445 }
446
447 // Handles the device attach event message on the blocking thread.
448 // |pnp_device_id| specifies the attached plug and play device ID string.
449 PortableDeviceWatcherWin::DeviceDetails
450 HandleDeviceAttachedEventOnBlockingThread(const string16& pnp_device_id) {
451 DCHECK(content::BrowserThread::
452 GetBlockingPool()->RunsTasksOnCurrentThread());
453 PortableDeviceWatcherWin::DeviceDetails device_details;
454 base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr;
455 HRESULT hr = portable_device_mgr.CreateInstance(
456 __uuidof(PortableDeviceManager), NULL, CLSCTX_INPROC_SERVER);
457 if (FAILED(hr)) {
458 // Either there is no portable device support (it must be a XP with
459 // Windows Media Player Version < 10) or the thread does not have COM
460 // initialized.
461 DCHECK_NE(CO_E_NOTINITIALIZED, hr);
462 return device_details;
vandebo (ex-Chrome) 2012/10/25 19:24:47 return PortabelDeviceWatcherWin::DeviceDetails() a
kmadhusu 2012/10/26 02:01:24 Modified the function to return void.
463 }
464 portable_device_mgr->RefreshDeviceList();
465 return GetDeviceInfoOnBlockingThread(portable_device_mgr, pnp_device_id);
466 }
467
468 // Constructs and returns a storage path from storage unique identifier.
469 string16 GetStoragePathFromStorageId(const std::string& storage_unique_id) {
470 // Construct a dummy device path using the storage name. This is only used
471 // for registering device media file system.
472 DCHECK(!storage_unique_id.empty());
473 std::string root_path("\\\\");
vandebo (ex-Chrome) 2012/10/25 19:24:47 nit: const
kmadhusu 2012/10/26 02:01:24 Done.
474 return UTF8ToUTF16(root_path + storage_unique_id);
475 }
476
477 } // namespace
478
479 PortableDeviceWatcherWin::PortableDeviceWatcherWin()
480 : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
481 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
482 content::NotificationService::AllSources());
483 }
484
485 PortableDeviceWatcherWin::~PortableDeviceWatcherWin() {
486 }
487
488 void PortableDeviceWatcherWin::Init() {
489 if (app_terminating_flag_.IsSet())
490 return;
491
492 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
493 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
494 base::SequencedWorkerPool::SequenceToken media_sequence_token =
495 pool->GetNamedSequenceToken(kMediaTaskRunnerName);
496 media_task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior(
497 media_sequence_token, base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
498 DCHECK(media_task_runner_.get());
499 EnumerateAttachedDevices();
500 }
501
502 bool PortableDeviceWatcherWin::GetDeviceInfo(const FilePath& device_path,
vandebo (ex-Chrome) 2012/10/25 19:24:47 Remove this method since it's not needed now and i
kmadhusu 2012/10/26 02:01:24 Done.
503 string16* location,
504 std::string* unique_id,
505 string16* name,
506 bool* removable) {
507 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
508 NOTIMPLEMENTED();
509 return false;
510 }
511
512 void PortableDeviceWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) {
513 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
514 if (app_terminating_flag_.IsSet())
515 return;
516
517 switch (event_type) {
518 case DBT_DEVICEARRIVAL: {
519 if (IsPortableDeviceStructure(data))
520 HandleDeviceAttachEvent(GetPnpDeviceId(data));
521 break;
522 }
523 case DBT_DEVICEREMOVECOMPLETE: {
524 if (IsPortableDeviceStructure(data))
525 HandleDeviceDetachEvent(GetPnpDeviceId(data));
526 break;
527 }
528 }
529 }
530
531 void PortableDeviceWatcherWin::EnumerateAttachedDevices() {
532 DCHECK(media_task_runner_.get());
533 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
534 if (app_terminating_flag_.IsSet())
535 return;
536
537 base::PostTaskAndReplyWithResult(
538 media_task_runner_,
539 FROM_HERE,
540 base::Bind(&EnumerateAttachedStoragesOnBlockingThread),
541 base::Bind(&PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices,
542 weak_ptr_factory_.GetWeakPtr()));
543 }
544
545 void PortableDeviceWatcherWin::HandleDeviceAttachEvent(
546 const string16& pnp_device_id) {
547 DCHECK(media_task_runner_.get());
548 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
549 if (app_terminating_flag_.IsSet())
550 return;
551
552 base::PostTaskAndReplyWithResult(
553 media_task_runner_,
554 FROM_HERE,
555 base::Bind(&HandleDeviceAttachedEventOnBlockingThread, pnp_device_id),
556 base::Bind(&PortableDeviceWatcherWin::OnHandleDeviceAttachEvent,
557 weak_ptr_factory_.GetWeakPtr()));
558 }
559
560 void PortableDeviceWatcherWin::HandleDeviceDetachEvent(
561 const string16& pnp_device_id) {
562 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
563 MtpDeviceMap::iterator device_entry = device_map_.find(pnp_device_id);
564 if (device_entry == device_map_.end())
565 return;
566
567 base::SystemMonitor* system_monitor = base::SystemMonitor::Get();
568 DCHECK(system_monitor);
569
570 StorageInfoList storage_info_list = device_entry->second;
571 for (size_t index = 0; index < storage_info_list.size(); ++index) {
572 std::string storage_id = storage_info_list[index].unique_id;
573 MtpStorageMap::iterator storage_entry = storage_map_.find(storage_id);
574 DCHECK(storage_entry != storage_map_.end());
575 system_monitor->ProcessRemovableStorageDetached(
576 storage_entry->second.device_id);
577 storage_map_.erase(storage_entry);
578 }
579 device_map_.erase(device_entry);
580 }
581
582 void PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices(
583 std::vector<DeviceDetails> device_details_list) {
584 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
585 for (size_t index = 0; index < device_details_list.size(); ++index)
586 OnHandleDeviceAttachEvent(device_details_list[index]);
587 }
588
589 void PortableDeviceWatcherWin::OnHandleDeviceAttachEvent(
590 DeviceDetails device_details) {
591 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
592 if (app_terminating_flag_.IsSet())
593 return;
594
595 const StorageInfoList& storage_info_list = device_details.storage_info_list;
596 const string16& name = device_details.name;
597 const string16& location = device_details.location;
598 DCHECK(!ContainsKey(device_map_, location));
599 base::SystemMonitor* system_monitor = base::SystemMonitor::Get();
600 DCHECK(system_monitor);
601 for (size_t index = 0; index < storage_info_list.size(); ++index) {
602 const std::string& storage_id = storage_info_list[index].unique_id;
603 DCHECK(!ContainsKey(storage_map_, storage_id));
604
605 // Keep track of storage id and storage name to see how often we receive
606 // empty values.
607 MediaStorageUtil::RecordDeviceInfoHistogram(false, storage_id, name);
608 if (storage_id.empty() || name.empty())
609 return;
610
611 // Device can have several data storage_info_list. Therefore, add the
612 // partition details to the storage name. E.g.: "Nexus 7 (s10001)"
613 string16 storage_name(name + UTF8ToUTF16(kSpaceDelim) +
614 UTF8ToUTF16(kLeftParen) +
615 storage_info_list[index].storage_object_id +
616 UTF8ToUTF16(kRightParen));
Peter Kasting 2012/10/25 05:23:24 See comments above regarding nuking these constant
kmadhusu 2012/10/26 02:01:24 Done.
617
618 base::SystemMonitor::RemovableStorageInfo storage_info(storage_id,
vandebo (ex-Chrome) 2012/10/25 19:24:47 nit: combine line 618 and 621
kmadhusu 2012/10/26 02:01:24 Done.
619 storage_name,
620 location);
621 storage_map_[storage_id] = storage_info;
622 system_monitor->ProcessRemovableStorageAttached(
623 storage_id, storage_name, GetStoragePathFromStorageId(storage_id));
624 }
625 device_map_[location] = storage_info_list;
626 }
627
628 void PortableDeviceWatcherWin::Observe(
629 int type,
630 const content::NotificationSource& source,
631 const content::NotificationDetails& details) {
632 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
633 DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type);
634 app_terminating_flag_.Set();
635 }
636
637 } // namespace chrome
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698