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

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, 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/i18n/case_conversion.h"
12 #include "base/logging.h"
13 #include "base/stl_util.h"
14 #include "base/stringprintf.h"
15 #include "base/threading/sequenced_worker_pool.h"
16 #include "base/utf_string_conversions.h"
17 #include "base/win/scoped_co_mem.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/notification_service.h"
22
23 namespace chrome {
24
25 using base::Bind;
26 using base::SequencedTaskRunner;
27 using base::SystemMonitor;
28 using base::win::ScopedCoMem;
29 using base::win::ScopedComPtr;
30 using chrome::MediaStorageUtil;
31 using content::BrowserThread;
Peter Kasting 2012/10/19 21:31:12 Please avoid using statements unless they save a g
kmadhusu 2012/10/23 23:44:17 Done.
32
33 namespace {
34
35 // Name of the client application that communicates with the mtp device.
36 const char16 kClientName[] = L"Chromium";
37
38 // Returns true if |data| represents a class of portable devices.
39 bool IsPortableDeviceStructure(LPARAM data) {
40 DEV_BROADCAST_HDR* broadcast_hdr =
41 reinterpret_cast<DEV_BROADCAST_HDR*>(data);
42 if (!broadcast_hdr ||
43 broadcast_hdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE) {
Peter Kasting 2012/10/19 21:31:12 Nit: {} not necessary with one-line conditional bo
kmadhusu 2012/10/23 23:44:17 If the condition spans over a line, I thought it i
Peter Kasting 2012/10/24 00:06:32 It's not banned, but it's not required either.
44 return false;
45 }
46
47 GUID guidDevInterface = GUID_NULL;
48 if (FAILED(CLSIDFromString(kWPDDevInterfaceGUID, &guidDevInterface)))
49 return false;
50 DEV_BROADCAST_DEVICEINTERFACE* dev_interface =
51 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data);
52 return (IsEqualGUID(dev_interface->dbcc_classguid, guidDevInterface) != 0);
53 }
54
55 // Returns the portable device plug and play device ID string.
56 string16 GetPnpDeviceId(LPARAM data) {
57 DEV_BROADCAST_DEVICEINTERFACE* dev_interface =
58 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data);
59 if (!dev_interface)
60 return string16();
61 return base::i18n::ToLower(dev_interface->dbcc_name);
Peter Kasting 2012/10/19 21:31:12 Is ToLower() really appropriate here? It's locale
kmadhusu 2012/10/23 23:44:17 When a mtp device is attached, DEV_BROADCAST_DEVIC
Peter Kasting 2012/10/24 00:06:32 It's correct as long as you're guaranteed that the
62 }
63
64 // Returns the friendly name of the device specified by the |pnp_device_id|.
65 string16 GetFriendlyName(
66 const string16& pnp_device_id,
67 const IPortableDeviceManager* portable_device_mgr) {
68 DWORD name_len = 0;
69 LPWSTR device_id = const_cast<char16*>(pnp_device_id.c_str());
70 IPortableDeviceManager* device_manager =
71 const_cast<IPortableDeviceManager*>(portable_device_mgr);
Peter Kasting 2012/10/19 21:31:12 Don't const-cast. Take a non-const pointer. If t
kmadhusu 2012/10/23 23:44:17 Done.
72 HRESULT hr = device_manager->GetDeviceFriendlyName(device_id, NULL,
73 &name_len);
74 if (FAILED(hr))
75 return string16();
76
77 scoped_array<char16> friendly_name(new char16[name_len]);
78 ZeroMemory(friendly_name.get(), name_len);
Peter Kasting 2012/10/19 21:31:12 Don't do this... use WriteInto() with a standard s
kmadhusu 2012/10/23 23:44:17 Done.
79 hr = device_manager->GetDeviceFriendlyName(device_id, friendly_name.get(),
80 &name_len);
81 if (FAILED(hr))
82 return string16();
83 return string16(friendly_name.get());
Peter Kasting 2012/10/19 21:31:12 Nit: No need for explicit string16 conversions (se
kmadhusu 2012/10/23 23:44:17 Done.
84 }
85
86 // Returns the manufacturer name of the device specified by the
87 // |pnp_device_id|.
88 string16 GetManufacturerName(
89 const string16& pnp_device_id,
90 const IPortableDeviceManager* portable_device_mgr) {
91 DWORD name_len = 0;
92 LPWSTR device_id = const_cast<char16*>(pnp_device_id.c_str());
93 IPortableDeviceManager* device_manager =
94 const_cast<IPortableDeviceManager*>(portable_device_mgr);
95 HRESULT hr = device_manager->GetDeviceManufacturer(device_id, NULL,
96 &name_len);
97 if (FAILED(hr))
98 return string16();
99
100 scoped_array<char16> manufacturer_name(new char16[name_len]);
101 ZeroMemory(manufacturer_name.get(), name_len);
102 hr = device_manager->GetDeviceManufacturer(device_id,
103 manufacturer_name.get(),
104 &name_len);
105 if (FAILED(hr))
106 return string16();
107 return string16(manufacturer_name.get());
108 }
109
110 // Returns the description of the device specified by the |pnp_device_id|.
111 string16 GetDeviceDescription(
112 const string16& pnp_device_id,
113 const IPortableDeviceManager* portable_device_mgr) {
114 DWORD desc_len = 0;
115 LPWSTR device_id = const_cast<char16*>(pnp_device_id.c_str());
116 IPortableDeviceManager* device_manager =
117 const_cast<IPortableDeviceManager*>(portable_device_mgr);
118 HRESULT hr = device_manager->GetDeviceDescription(device_id, NULL,
119 &desc_len);
120 if (FAILED(hr))
121 return string16();
122
123 scoped_array<char16> description(new char16[desc_len]);
124 ZeroMemory(description.get(), desc_len);
125 hr = device_manager->GetDeviceDescription(device_id, description.get(),
126 &desc_len);
127 if (FAILED(hr))
128 return string16();
129 return string16(description.get());
130 }
131
132 // On success, returns true and updates |client_info| with a reference to an
133 // IPortableDeviceValues interface that holds information about the
134 // application that communicates with the device.
135 bool GetClientInformation(ScopedComPtr<IPortableDeviceValues>* client_info) {
136 HRESULT hr = client_info->CreateInstance(__uuidof(PortableDeviceValues),
137 NULL, CLSCTX_INPROC_SERVER);
138 if (FAILED(hr)) {
139 DPLOG(ERROR) << "Failed to create an instance of IPortableDeviceValues";
Peter Kasting 2012/10/19 21:31:12 I've never even heard of DPLOG. What the heck is
kmadhusu 2012/10/23 23:44:17 DPLOG appends the last system error (GetLastError(
140 return false;
141 }
142
143 hr = (*client_info)->SetStringValue(WPD_CLIENT_NAME, kClientName);
144 if (FAILED(hr))
145 DPLOG(ERROR) << "Failed to set client name";
146
147 hr = (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, 0);
148 if (FAILED(hr))
149 DPLOG(ERROR) << "Failed to set client major version";
150
151 hr = (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, 0);
152 if (FAILED(hr))
153 DPLOG(ERROR) << "Failed to set client minor version";
154
155 hr = (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_REVISION, 0);
156 if (FAILED(hr))
157 DPLOG(ERROR) << "Failed to set client revision";
158
159 hr = (*client_info)->SetUnsignedIntegerValue(
160 WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE, SECURITY_IMPERSONATION);
161 if (FAILED(hr))
162 DPLOG(ERROR) << "Failed to set security quality of service";
163
164 hr = (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS,
165 GENERIC_READ);
166 if (FAILED(hr))
167 DPLOG(ERROR) << "Failed to set client desired access value";
168 return true;
Peter Kasting 2012/10/19 21:31:12 It seems strange that we check for a bunch of fail
kmadhusu 2012/10/23 23:44:17 These calls can fail. It is not mandatory to set a
169 }
170
171 // Opens the device for communication. |pnp_device_id| specifies the plug and
172 // play device ID string. On success, returns true and updates |device| with a
173 // reference to the portable device interface.
174 bool Setup(const string16& pnp_device_id,
175 ScopedComPtr<IPortableDevice>* device) {
176 ScopedComPtr<IPortableDeviceValues> client_info;
177 if (!GetClientInformation(&client_info))
178 return false;
179
180 HRESULT hr = device->CreateInstance(__uuidof(PortableDevice), NULL,
181 CLSCTX_INPROC_SERVER);
182 if (FAILED(hr)) {
183 DPLOG(ERROR) << "Failed to create an instance of IPortableDevice";
184 return false;
185 }
186
187 LPWSTR device_id = const_cast<char16*>(pnp_device_id.c_str());
Peter Kasting 2012/10/19 21:31:12 Again, don't const_cast. In this case MSDN claims
kmadhusu 2012/10/23 23:44:17 Done.
188 hr = (*device)->Open(device_id, client_info.get());
189 if (FAILED(hr)) {
190 if (hr == E_ACCESSDENIED)
191 DPLOG(ERROR) << "Access denied to open the device";
192 return false;
193 }
194 return true;
195 }
196
197 // Returns true if the object specified by the |object_id| is the device root
198 // object.
199 bool IsDeviceObjectId(const string16& object_id) {
200 return object_id == WPD_DEVICE_OBJECT_ID;
201 }
202
203 // Returns the unique id property key of the object specified by the
204 // |object_id|.
205 REFPROPERTYKEY GetUniqueIdPropertyKey(const string16& object_id) {
206 if (IsDeviceObjectId(object_id))
Peter Kasting 2012/10/19 21:31:12 Nit: Or just return IsDeviceObjectId(object_id)
kmadhusu 2012/10/23 23:44:17 Done.
207 return WPD_DEVICE_SERIAL_NUMBER;
208 return WPD_OBJECT_PERSISTENT_UNIQUE_ID;
209 }
210
211 // On success, returns true and populates |properties_to_read| with the
212 // property key of the object specified by the |object_id|.
213 bool PopulatePropertyKeyCollection(
214 const string16& object_id,
215 ScopedComPtr<IPortableDeviceKeyCollection>* properties_to_read) {
216 HRESULT hr = properties_to_read->CreateInstance(
217 __uuidof(PortableDeviceKeyCollection), NULL, CLSCTX_INPROC_SERVER);
218 if (FAILED(hr)) {
219 DPLOG(ERROR) << "Failed to create IPortableDeviceKeyCollection instance";
220 return false;
221 }
222 REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id);
223 hr = (*properties_to_read)->Add(key);
224 return SUCCEEDED(hr);
225 }
226
227 // Wrapper function to get content property string value.
228 bool GetStringPropertyValue(
229 const ScopedComPtr<IPortableDeviceValues>& properties_values,
Peter Kasting 2012/10/19 21:31:12 Nit: Passing ScopedComPtr by const ref is weird.
kmadhusu 2012/10/23 23:44:17 When I pass "const IPortableDeviceValues&", I need
230 REFPROPERTYKEY key, string16* value) {
231 ScopedCoMem<char16> buffer;
232 HRESULT hr = properties_values->GetStringValue(key, &buffer);
233 if (FAILED(hr))
234 return false;
235
236 if (value)
237 *value = string16(buffer);
238 return true;
239 }
240
241 // Constructs a unique identifier for the object specified by the |object_id|.
242 // On success, returns true and fills in |unique_id|.
243 bool GetObjectUniqueId(const ScopedComPtr<IPortableDevice>& device,
244 const string16& object_id,
245 string16* unique_id) {
246 ScopedComPtr<IPortableDeviceContent> content;
247 HRESULT hr = device->Content(content.Receive());
248 if (FAILED(hr)) {
249 DPLOG(ERROR) << "Failed to get IPortableDeviceContent interface";
250 return false;
251 }
252
253 ScopedComPtr<IPortableDeviceProperties> properties;
254 hr = content->Properties(properties.Receive());
255 if (FAILED(hr)) {
256 DPLOG(ERROR) << "Failed to get IPortableDeviceProperties interface";
257 return false;
258 }
259
260 ScopedComPtr<IPortableDeviceKeyCollection> properties_to_read;
261 if (!PopulatePropertyKeyCollection(object_id, &properties_to_read))
262 return false;
263
264 ScopedComPtr<IPortableDeviceValues> properties_values;
265 hr = properties->GetValues(const_cast<char16*>(object_id.c_str()),
266 properties_to_read.get(),
267 properties_values.Receive());
268 if (FAILED(hr)) {
269 DPLOG(ERROR) << "Failed to get property values";
270 return false;
271 }
272
273 REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id);
274 return GetStringPropertyValue(properties_values, key, unique_id);
275 }
276
277 // Constructs the device storage unique identifier using |device_serial_num| and
278 // |storage_id|. On success, returns true and fills in |device_storage_id|.
279 bool GetDeviceStorageUniqueId(const string16& device_serial_num,
280 const string16& storage_id,
281 std::string* device_storage_id) {
282 if (device_serial_num.empty() && storage_id.empty())
283 return false;
284
285 // |unique_id| format: StorageSerial:|storage_id|:|device_serial_num|
286 string16 unique_id = base::StringPrintf(L"%ls%ls%ls%ls",
Peter Kasting 2012/10/19 21:31:12 Nit: I find it clearer to just do: string16 uni
kmadhusu 2012/10/23 23:44:17 I would like to leave it as it is. I have added th
Peter Kasting 2012/10/24 00:06:32 Are you sure %ls is correct on non-Windows, then?
287 chrome::kMtpDeviceStorageIdPrefix,
288 storage_id.c_str(),
289 chrome::kNonSpaceDelim,
290 device_serial_num.c_str());
291
292 // |device_storage_id| format: mtp:|unique_id|
293 *device_storage_id = MediaStorageUtil::MakeDeviceId(
294 MediaStorageUtil::MTP_OR_PTP, UTF16ToUTF8(unique_id));
295 return true;
296 }
297
298 // Gets a list of removable storage object identifiers present in |device|.
299 // On success, returns true and fills in |storage_ids|.
300 bool GetRemovableStorageObjectIds(const ScopedComPtr<IPortableDevice>& device,
301 std::vector<string16>* storage_ids) {
302 ScopedComPtr<IPortableDeviceCapabilities> capabilities;
303 HRESULT hr = device->Capabilities(capabilities.Receive());
304 if (FAILED(hr)) {
305 DPLOG(ERROR) << "Failed to get IPortableDeviceCapabilities interface";
306 return false;
307 }
308
309 ScopedComPtr<IPortableDevicePropVariantCollection> storage_obj_ids;
310 hr = capabilities->GetFunctionalObjects(WPD_FUNCTIONAL_CATEGORY_STORAGE,
311 storage_obj_ids.Receive());
312 if (FAILED(hr)) {
313 DPLOG(ERROR) << "Failed to get IPortableDevicePropVariantCollection";
314 return false;
315 }
316
317 DWORD num_storage_obj_ids = 0;
318 hr = storage_obj_ids->GetCount(&num_storage_obj_ids);
319 if (FAILED(hr))
320 return false;
321
322 for (DWORD index = 0; index < num_storage_obj_ids; ++index) {
323 PROPVARIANT object_id = {0};
324 PropVariantInit(&object_id);
325 hr = storage_obj_ids->GetAt(index, &object_id);
326 if (SUCCEEDED(hr) && object_id.pwszVal != NULL &&
327 object_id.vt == VT_LPWSTR) {
328 storage_ids->push_back(object_id.pwszVal);
329 }
330 PropVariantClear(&object_id);
331 }
332 return true;
333 }
334
335 } // namespace
336
337 PortableDeviceWatcherWin::PortableDeviceWatcherWin()
338 : on_shutdown_event_(true, false) {
339 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
340 content::NotificationService::AllSources());
341 }
342
343 void PortableDeviceWatcherWin::Init() {
344 base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
345 base::SequencedWorkerPool::SequenceToken media_sequence_token =
346 pool->GetNamedSequenceToken("media-task-runner");
347 media_task_runner_ = pool->GetSequencedTaskRunner(media_sequence_token);
348 DCHECK(media_task_runner_);
349
350 media_task_runner_->PostTask(
351 FROM_HERE, Bind(&PortableDeviceWatcherWin::InitOnBlockingThread, this));
352 }
353
354 bool PortableDeviceWatcherWin::GetDeviceInfo(const FilePath& device_path,
355 string16* location,
356 std::string* unique_id,
357 string16* name,
358 bool* removable) {
359 NOTIMPLEMENTED();
360 return false;
361 }
362
363 void PortableDeviceWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) {
Peter Kasting 2012/10/19 21:31:12 I can't find anywhere that actually calls this or
kmadhusu 2012/10/23 23:44:17 It is called from RemovableDeviceNotificationsWind
364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
365 switch (event_type) {
366 case DBT_DEVICEARRIVAL: {
367 if (IsPortableDeviceStructure(data)) {
368 DCHECK(media_task_runner_.get());
369 media_task_runner_->PostTask(
370 FROM_HERE,
371 Bind(&PortableDeviceWatcherWin::
372 HandleDeviceAttachEventOnBlockingThread,
373 this, GetPnpDeviceId(data)));
374 }
375 break;
376 }
377 case DBT_DEVICEREMOVECOMPLETE: {
378 if (IsPortableDeviceStructure(data))
379 HandleDeviceDetachEventOnUIThread(GetPnpDeviceId(data));
380 break;
381 }
382 }
383 }
384
385 PortableDeviceWatcherWin::~PortableDeviceWatcherWin() {
386 registrar_.RemoveAll();
Peter Kasting 2012/10/19 21:31:12 Nit: Not necessary, this happens automatically
kmadhusu 2012/10/23 23:44:17 Removed.
387 }
388
389 void PortableDeviceWatcherWin::InitOnBlockingThread() {
390 DCHECK(media_task_runner_->RunsTasksOnCurrentThread());
391 if (on_shutdown_event_.IsSignaled()) {
392 // Process is in shut down mode.
393 return;
394 }
395 DCHECK(!portable_device_mgr_.get());
396 HRESULT hr = portable_device_mgr_.CreateInstance(
397 __uuidof(PortableDeviceManager), NULL, CLSCTX_INPROC_SERVER);
398 if (FAILED(hr)) {
399 // Either there is no portable device support (it must be a XP with
400 // Windows Media Player Version < 10) or the thread does not have COM
401 // initialized.
402 if (hr == CO_E_NOTINITIALIZED)
Peter Kasting 2012/10/19 21:31:12 Nit: DCHECK_NE(CO_E_NOTINITIALIZED, hr);
kmadhusu 2012/10/23 23:44:17 Done.
403 NOTREACHED();
404 return;
405 }
406
407 EnumerateStoragesOnBlockingThread();
408 }
409
410 string16 PortableDeviceWatcherWin::GetDeviceName(
411 const string16& pnp_device_id) {
412 DCHECK(media_task_runner_->RunsTasksOnCurrentThread());
413 if (on_shutdown_event_.IsSignaled()) {
414 // Process is in shut down mode.
415 return string16();
416 }
417
418 DCHECK(portable_device_mgr_.get());
419 string16 name = GetFriendlyName(pnp_device_id, portable_device_mgr_.get());
Peter Kasting 2012/10/19 21:31:12 Nit: If you change these functions to return a boo
kmadhusu 2012/10/23 23:44:17 Condensed the code as suggested.
420 if (!name.empty())
421 return name;
422
423 name = GetDeviceDescription(pnp_device_id, portable_device_mgr_.get());
424 if (!name.empty())
425 return name;
426
427 return GetManufacturerName(pnp_device_id, portable_device_mgr_.get());
428 }
429
430 bool PortableDeviceWatcherWin::GetStorages(
431 const string16& pnp_device_id,
432 std::vector<DeviceStorageInfo>* storages) {
433 DCHECK(media_task_runner_->RunsTasksOnCurrentThread());
434 if (on_shutdown_event_.IsSignaled()) {
435 // Process is in shut down mode.
436 return false;
437 }
438
439 ScopedComPtr<IPortableDevice> device;
440 if (!Setup(pnp_device_id, &device))
441 return false;
442
443 std::vector<string16> storage_obj_ids;
444 if (!GetRemovableStorageObjectIds(device, &storage_obj_ids))
445 return false;
446
447 // Get the device serial number (E.g.: 4889033500677371).
448 string16 device_serial_num;
449 if (!GetObjectUniqueId(device, WPD_DEVICE_OBJECT_ID, &device_serial_num))
450 return false;
451
452 for (size_t index = 0; index < storage_obj_ids.size(); ++index) {
453 // Get the storage object persistent id (E.g.: SID-{10001,D,31080448}).
454 string16 storage_unique_id;
455 if (!GetObjectUniqueId(device, storage_obj_ids[index],
456 &storage_unique_id)) {
457 continue;
458 }
459 std::string device_storage_id;
460 if (GetDeviceStorageUniqueId(device_serial_num, storage_unique_id,
461 &device_storage_id)) {
462 DeviceStorageInfo storage_info;
463 storage_info.storage_object_id = storage_obj_ids[index];
464 storage_info.unique_id = device_storage_id;
465 storages->push_back(storage_info);
466 }
467 }
468 return true;
469 }
470
471 void PortableDeviceWatcherWin::EnumerateStoragesOnBlockingThread() {
472 DCHECK(media_task_runner_->RunsTasksOnCurrentThread());
473 if (on_shutdown_event_.IsSignaled()) {
474 // Process is in shut down mode.
475 return;
476 }
477
478 DCHECK(portable_device_mgr_.get());
479
480 // Get the total number of devices found on the system.
481 DWORD pnp_device_count = 0;
482 HRESULT hr = portable_device_mgr_->GetDevices(NULL, &pnp_device_count);
483 if (FAILED(hr))
484 return;
485
486 scoped_array<LPWSTR> pnp_device_ids(new LPWSTR[pnp_device_count]);
487 ZeroMemory(pnp_device_ids.get(), pnp_device_count);
488 hr = portable_device_mgr_->GetDevices(
489 reinterpret_cast<LPWSTR*>(pnp_device_ids.get()), &pnp_device_count);
490 if (FAILED(hr))
491 return;
492
493 for (DWORD index = 0; index < pnp_device_count; ++index) {
Peter Kasting 2012/10/19 21:31:12 Nit: {} unnecessary
kmadhusu 2012/10/23 23:44:17 Done.
494 GetDeviceInfoOnBlockingThread(pnp_device_ids[index]);
495 }
496 }
497
498 void PortableDeviceWatcherWin::HandleDeviceAttachEventOnBlockingThread(
499 const string16& pnp_device_id) {
500 DCHECK(media_task_runner_->RunsTasksOnCurrentThread());
501
502 if (on_shutdown_event_.IsSignaled()) {
503 // Process is in shut down mode.
504 return;
505 }
506
507 // |portable_device_mgr_| is uninitialized during unit tests.
508 if (portable_device_mgr_.get())
509 portable_device_mgr_->RefreshDeviceList();
510 GetDeviceInfoOnBlockingThread(pnp_device_id);
511 }
512
513 void PortableDeviceWatcherWin::HandleDeviceDetachEventOnUIThread(
514 const string16& pnp_device_id) {
515 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
516 MtpDeviceMap::iterator device_entry = device_map_.find(pnp_device_id);
517 if (device_entry == device_map_.end())
518 return;
519
520 SystemMonitor* system_monitor = SystemMonitor::Get();
521 DCHECK(system_monitor);
522
523 StorageInfoList storages = device_entry->second;
524 for (size_t index = 0; index < storages.size(); ++index) {
525 std::string storage_id = storages[index].unique_id;
526 MtpStorageMap::iterator storage_entry = storage_map_.find(storage_id);
527 if (storage_entry == storage_map_.end()) {
528 NOTREACHED();
529 continue;
Peter Kasting 2012/10/19 21:31:12 Don't handle assertion failure. This block should
kmadhusu 2012/10/23 23:44:17 DCHECK_NE doesn't seem to work with iterators. Mod
530 }
531 system_monitor->ProcessRemovableStorageDetached(
532 storage_entry->second.device_id);
533 storage_map_.erase(storage_entry);
534 }
535 device_map_.erase(device_entry);
536 }
537
538 void PortableDeviceWatcherWin::GetDeviceInfoOnBlockingThread(
539 const string16& pnp_device_id) {
540 DCHECK(media_task_runner_->RunsTasksOnCurrentThread());
541 DCHECK(!pnp_device_id.empty());
542 string16 device_name = GetDeviceName(pnp_device_id);
543 StorageInfoList storages;
544 if (!GetStorages(pnp_device_id, &storages))
545 return;
546
547 if (on_shutdown_event_.IsSignaled()) {
Peter Kasting 2012/10/19 21:31:12 Nit: Shouldn't this be checked before we make any
kmadhusu 2012/10/23 23:44:17 Done.
548 // Process is in shut down mode.
549 return;
550 }
551
552 BrowserThread::PostTask(
553 BrowserThread::UI, FROM_HERE,
554 Bind(&PortableDeviceWatcherWin::AddMtpDeviceOnUIThread,
555 this, storages, device_name, pnp_device_id));
556 }
557
558 void PortableDeviceWatcherWin::AddMtpDeviceOnUIThread(
559 const StorageInfoList& storages,
560 const string16& name,
561 const string16& location) {
562 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
563 DCHECK(!ContainsKey(device_map_, location));
564 SystemMonitor* system_monitor = SystemMonitor::Get();
565 DCHECK(system_monitor);
566 for (size_t index = 0; index < storages.size(); ++index) {
567 const std::string& storage_id = storages[index].unique_id;
568 DCHECK(!ContainsKey(storage_map_, storage_id));
569
570 // Keep track of storage id and storage name to see how often we receive
571 // empty values.
572 MediaStorageUtil::RecordDeviceInfoHistogram(false, storage_id, name);
573 if (storage_id.empty() || name.empty())
574 return;
575
576 // Device can have several data storages. Therefore, add the partition
577 // details to the storage name.
578 // E.g.: "Nexus 7 (SID-{10001,D,31080448})"
579 string16 storage_name = base::StringPrintf(
580 L"%ls (%ls)", name.c_str(), storages[index].storage_object_id.c_str());
581
582 SystemMonitor::RemovableStorageInfo storage_info(storage_id, storage_name,
583 location);
584 storage_map_[storage_id] = storage_info;
585 system_monitor->ProcessRemovableStorageAttached(storage_id, storage_name,
586 location);
587 }
588 device_map_[location] = storages;
589 }
590
591 void PortableDeviceWatcherWin::Observe(
592 int type,
593 const content::NotificationSource& source,
594 const content::NotificationDetails& details) {
595 DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type);
596 on_shutdown_event_.Signal();
597 }
598
599 } // namespace chrome
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698