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

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/logging.h"
12 #include "base/stl_util.h"
13 #include "base/string_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 "base/win/scoped_comptr.h"
19 #include "chrome/browser/system_monitor/media_storage_util.h"
20 #include "chrome/browser/system_monitor/removable_device_constants.h"
21 #include "chrome/common/chrome_notification_types.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/notification_service.h"
24
25 namespace chrome {
26
27 namespace {
28
29 // Name of the client application that communicates with the mtp device.
30 const char16 kClientName[] = L"Chromium";
31
32 // Name of the sequenced task runner.
33 const char kMediaTaskRunnerName[] = "media-task-runner";
34
35 // Returns true if |data| represents a class of portable devices.
36 bool IsPortableDeviceStructure(LPARAM data) {
37 DEV_BROADCAST_HDR* broadcast_hdr =
38 reinterpret_cast<DEV_BROADCAST_HDR*>(data);
39 if (!broadcast_hdr ||
40 (broadcast_hdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)) {
41 return false;
42 }
43
44 GUID guidDevInterface = GUID_NULL;
45 if (FAILED(CLSIDFromString(kWPDDevInterfaceGUID, &guidDevInterface)))
46 return false;
47 DEV_BROADCAST_DEVICEINTERFACE* dev_interface =
48 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data);
49 return (IsEqualGUID(dev_interface->dbcc_classguid, guidDevInterface) != 0);
50 }
51
52 // Returns the portable device plug and play device ID string.
53 string16 GetPnpDeviceId(LPARAM data) {
54 DEV_BROADCAST_DEVICEINTERFACE* dev_interface =
55 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data);
56 if (!dev_interface)
57 return string16();
58 DCHECK(IsStringASCII(dev_interface->dbcc_name));
59 return StringToLowerASCII(string16(dev_interface->dbcc_name));
Peter Kasting 2012/10/25 05:23:24 Nit: Explicit conversion to string16 unnecessary,
kmadhusu 2012/10/26 02:01:24 If I don't have string16, compiler complaints that
Peter Kasting 2012/10/26 02:14:49 From void? That doesn't make sense, you can't con
kmadhusu 2012/10/26 22:23:26 StringToLowerASCII template will not work with w_c
60 }
61
62 // Gets the friendly name of the device specified by the |pnp_device_id|. On
63 // success, returns true and fills in |name|.
64 bool GetFriendlyName(const string16& pnp_device_id,
65 IPortableDeviceManager* device_manager,
66 string16* name) {
Peter Kasting 2012/10/25 05:23:24 Nit: Add DCHECK(device_manager); to make it clear
kmadhusu 2012/10/26 02:01:24 Done.
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 string16 friendly_name;
74 hr = device_manager->GetDeviceFriendlyName(
75 pnp_device_id.c_str(), WriteInto(&friendly_name, name_len), &name_len);
76 if (FAILED(hr))
77 return false;
78
79 if (name)
Peter Kasting 2012/10/25 05:23:24 Nit: Is this ever NULL? If not, DCHECK it atop th
kmadhusu 2012/10/26 02:01:24 As of now, this function is always called with a v
80 *name = friendly_name;
81 return !friendly_name.empty();
82 }
83
84 // Gets the manufacturer name of the device specified by the |pnp_device_id|.
85 // On success, returns true and fills in |name|.
86 bool GetManufacturerName(const string16& pnp_device_id,
87 IPortableDeviceManager* device_manager,
88 string16* name) {
89 DWORD name_len = 0;
90 HRESULT hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(),
91 NULL, &name_len);
92 if (FAILED(hr))
93 return false;
94
95 string16 manufacturer_name;
96 hr = device_manager->GetDeviceManufacturer(
97 pnp_device_id.c_str(), WriteInto(&manufacturer_name, name_len),
98 &name_len);
99 if (FAILED(hr))
100 return false;
101
102 if (name)
103 *name = manufacturer_name;
104 return !manufacturer_name.empty();
105 }
106
107 // Gets the description of the device specified by the |pnp_device_id|. On
108 // success, returns true and fills in |name|.
109 bool GetDeviceDescription(const string16& pnp_device_id,
110 IPortableDeviceManager* device_manager,
111 string16* name) {
112 DWORD desc_len = 0;
113 HRESULT hr = device_manager->GetDeviceDescription(pnp_device_id.c_str(), NULL,
114 &desc_len);
115 if (FAILED(hr))
116 return false;
117
118 string16 description;
119 hr = device_manager->GetDeviceDescription(pnp_device_id.c_str(),
120 WriteInto(&description, desc_len),
121 &desc_len);
122 if (FAILED(hr))
123 return false;
124 if (name)
125 *name = description;
126 return !description.empty();
127 }
128
129 // On success, returns true and updates |client_info| with a reference to an
130 // IPortableDeviceValues interface that holds information about the
131 // application that communicates with the device.
132 bool GetClientInformation(
133 base::win::ScopedComPtr<IPortableDeviceValues>* client_info) {
134 HRESULT hr = client_info->CreateInstance(__uuidof(PortableDeviceValues),
135 NULL, CLSCTX_INPROC_SERVER);
136 if (FAILED(hr)) {
137 DPLOG(ERROR) << "Failed to create an instance of IPortableDeviceValues";
138 return false;
139 }
140
141 // Attempt to set client details.
142 (*client_info)->SetStringValue(WPD_CLIENT_NAME, kClientName);
143
Peter Kasting 2012/10/25 05:23:24 Nit: I'd remove all these blank lines
kmadhusu 2012/10/26 02:01:24 Done.
144 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, 0);
145
146 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, 0);
147
148 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_REVISION, 0);
149
150 (*client_info)->SetUnsignedIntegerValue(
151 WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE, SECURITY_IMPERSONATION);
152
153 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS,
154 GENERIC_READ);
155 return true;
156 }
157
158 // Opens the device for communication. |pnp_device_id| specifies the plug and
159 // play device ID string. On success, returns true and updates |device| with a
160 // reference to the portable device interface.
161 bool Setup(const string16& pnp_device_id,
162 base::win::ScopedComPtr<IPortableDevice>* device) {
163 base::win::ScopedComPtr<IPortableDeviceValues> client_info;
164 if (!GetClientInformation(&client_info))
165 return false;
166
167 HRESULT hr = device->CreateInstance(__uuidof(PortableDevice), NULL,
168 CLSCTX_INPROC_SERVER);
169 if (FAILED(hr)) {
170 DPLOG(ERROR) << "Failed to create an instance of IPortableDevice";
171 return false;
172 }
173
174 hr = (*device)->Open(pnp_device_id.c_str(), client_info.get());
175 if (FAILED(hr)) {
Peter Kasting 2012/10/25 05:23:24 Nit: If you early-return for SUCCEEDED(hr) instead
kmadhusu 2012/10/26 02:01:24 Done.
176 if (hr == E_ACCESSDENIED)
177 DPLOG(ERROR) << "Access denied to open the device";
178 return false;
179 }
180 return true;
181 }
182
183 // Returns true if the object specified by the |object_id| is the device root
184 // object.
185 bool IsDeviceObjectId(const string16& object_id) {
186 return (object_id == WPD_DEVICE_OBJECT_ID);
Peter Kasting 2012/10/25 05:23:24 Nit: Seems like this should just be inlined into t
kmadhusu 2012/10/26 02:01:24 Done.
187 }
188
189 // Returns the unique id property key of the object specified by the
190 // |object_id|.
191 REFPROPERTYKEY GetUniqueIdPropertyKey(const string16& object_id) {
192 return IsDeviceObjectId(object_id) ?
193 WPD_DEVICE_SERIAL_NUMBER : WPD_OBJECT_PERSISTENT_UNIQUE_ID;
194 }
195
196 // On success, returns true and populates |properties_to_read| with the
197 // property key of the object specified by the |object_id|.
198 bool PopulatePropertyKeyCollection(
199 const string16& object_id,
200 base::win::ScopedComPtr<IPortableDeviceKeyCollection>* properties_to_read) {
201 HRESULT hr = properties_to_read->CreateInstance(
202 __uuidof(PortableDeviceKeyCollection), NULL, CLSCTX_INPROC_SERVER);
203 if (FAILED(hr)) {
204 DPLOG(ERROR) << "Failed to create IPortableDeviceKeyCollection instance";
205 return false;
206 }
207 REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id);
208 hr = (*properties_to_read)->Add(key);
209 return SUCCEEDED(hr);
210 }
211
212 // Wrapper function to get content property string value.
213 bool GetStringPropertyValue(IPortableDeviceValues* properties_values,
214 REFPROPERTYKEY key,
215 string16* value) {
216 base::win::ScopedCoMem<char16> buffer;
217 HRESULT hr = properties_values->GetStringValue(key, &buffer);
218 if (FAILED(hr))
219 return false;
220
221 if (value)
222 *value = string16(buffer);
Peter Kasting 2012/10/25 05:23:24 Nit: Explicit conversion to string16 may not be ne
kmadhusu 2012/10/26 02:01:24 Done.
223 return true;
224 }
225
226 // Constructs a unique identifier for the object specified by the |object_id|.
227 // On success, returns true and fills in |unique_id|.
228 bool GetObjectUniqueId(IPortableDevice* device,
229 const string16& object_id,
230 string16* unique_id) {
231 base::win::ScopedComPtr<IPortableDeviceContent> content;
232 HRESULT hr = device->Content(content.Receive());
233 if (FAILED(hr)) {
234 DPLOG(ERROR) << "Failed to get IPortableDeviceContent interface";
235 return false;
236 }
237
238 base::win::ScopedComPtr<IPortableDeviceProperties> properties;
239 hr = content->Properties(properties.Receive());
240 if (FAILED(hr)) {
241 DPLOG(ERROR) << "Failed to get IPortableDeviceProperties interface";
242 return false;
243 }
244
245 base::win::ScopedComPtr<IPortableDeviceKeyCollection> properties_to_read;
246 if (!PopulatePropertyKeyCollection(object_id, &properties_to_read))
247 return false;
248
249 base::win::ScopedComPtr<IPortableDeviceValues> properties_values;
250 if (FAILED(properties->GetValues(object_id.c_str(),
251 properties_to_read.get(),
252 properties_values.Receive()))) {
253 return false;
254 }
255
256 REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id);
257 return GetStringPropertyValue(properties_values.get(), key, unique_id);
258 }
259
260 // Constructs the device storage unique identifier using |device_serial_num| and
261 // |storage_id|. On success, returns true and fills in |device_storage_id|.
262 bool GetDeviceStorageUniqueId(const string16& device_serial_num,
263 const string16& storage_id,
264 std::string* device_storage_id) {
265 if (device_serial_num.empty() && storage_id.empty())
266 return false;
267
268 // |unique_id| format: StorageSerial:|storage_id|:|device_serial_num|
269 string16 unique_id = base::StringPrintf(L"%ls%ls%ls%ls",
270 chrome::kMtpDeviceStorageIdPrefix,
271 storage_id.c_str(),
272 chrome::kNonSpaceDelim,
273 device_serial_num.c_str());
274
275 // |device_storage_id| format: mtp:|unique_id|
276 *device_storage_id = MediaStorageUtil::MakeDeviceId(
277 MediaStorageUtil::MTP_OR_PTP, UTF16ToUTF8(unique_id));
278 return true;
279 }
280
281 // Gets a list of removable storage object identifiers present in |device|.
282 // On success, returns true and fills in |storage_ids|.
283 bool GetRemovableStorageObjectIds(IPortableDevice* device,
284 std::vector<string16>* storage_ids) {
285 base::win::ScopedComPtr<IPortableDeviceCapabilities> capabilities;
286 HRESULT hr = device->Capabilities(capabilities.Receive());
287 if (FAILED(hr)) {
288 DPLOG(ERROR) << "Failed to get IPortableDeviceCapabilities interface";
289 return false;
290 }
291
292 base::win::ScopedComPtr<IPortableDevicePropVariantCollection> storage_obj_ids;
293 hr = capabilities->GetFunctionalObjects(WPD_FUNCTIONAL_CATEGORY_STORAGE,
294 storage_obj_ids.Receive());
295 if (FAILED(hr)) {
296 DPLOG(ERROR) << "Failed to get IPortableDevicePropVariantCollection";
297 return false;
298 }
299
300 DWORD num_storage_obj_ids = 0;
301 hr = storage_obj_ids->GetCount(&num_storage_obj_ids);
302 if (FAILED(hr))
303 return false;
304
305 for (DWORD index = 0; index < num_storage_obj_ids; ++index) {
306 PROPVARIANT object_id = {0};
307 PropVariantInit(&object_id);
308 hr = storage_obj_ids->GetAt(index, &object_id);
309 if (SUCCEEDED(hr) && (object_id.pwszVal != NULL) &&
310 (object_id.vt == VT_LPWSTR)) {
311 storage_ids->push_back(object_id.pwszVal);
312 }
313 PropVariantClear(&object_id);
314 }
315 return true;
316 }
317
318 // Returns true if the portable device is mounted on a volume. |device_name|
319 // specifies the name of the device.
320 bool IsVolumeMountedPortableDevice(const string16& device_name) {
321 // If the device is a volume mounted device, |device_name| will be
322 // the volume name.
323 return (device_name.length() >= 2) && (device_name[1] == L':') &&
324 ((device_name[0] >= L'A') && (device_name[0] <= L'Z') ||
Peter Kasting 2012/10/25 05:23:24 Nit: Indent 4, not even
kmadhusu 2012/10/26 02:01:24 Done.
325 ((device_name[0] >= L'a') && (device_name[0] <= L'z')));
326 }
327
328 // Returns the name of the device specified by |pnp_device_id|.
329 // Accessed on the blocking thread.
Peter Kasting 2012/10/25 05:23:24 Nit: Comment as to why this (and the other functio
kmadhusu 2012/10/26 02:01:24 Done.
330 string16 GetDeviceNameOnBlockingThread(
331 IPortableDeviceManager* portable_device_manager,
332 const string16& pnp_device_id) {
333 DCHECK(content::BrowserThread::
334 GetBlockingPool()->RunsTasksOnCurrentThread());
Peter Kasting 2012/10/25 05:23:24 Nit: Breaking after -> seems slightly better than
kmadhusu 2012/10/26 02:01:24 Done.
335 string16 name;
336 if (!(GetFriendlyName(pnp_device_id, portable_device_manager, &name) ||
Peter Kasting 2012/10/25 05:23:24 Nit: Can this just be: GetFriendlyName(...) ||
kmadhusu 2012/10/26 02:01:24 Done.
337 GetDeviceDescription(pnp_device_id, portable_device_manager,
338 &name) ||
339 GetManufacturerName(pnp_device_id, portable_device_manager,
340 &name))) {
341 return string16();
342 }
343 return name;
344 }
345
346 // Gets the device storage details on the blocking thread. On success, returns
347 // true and populates |storage_info_list| with device storage details.
348 bool GetDeviceStorageInfoListOnBlockingThread(
349 const string16& pnp_device_id,
350 std::vector<PortableDeviceWatcherWin::DeviceStorageInfo>*
351 storage_info_list) {
352 DCHECK(content::BrowserThread::
353 GetBlockingPool()->RunsTasksOnCurrentThread());
354 base::win::ScopedComPtr<IPortableDevice> device;
355 if (!Setup(pnp_device_id, &device))
356 return false;
357
358 std::vector<string16> storage_obj_ids;
359 if (!GetRemovableStorageObjectIds(device.get(), &storage_obj_ids))
360 return false;
361
362 // Get the device serial number (E.g.: 4889033500677371).
363 string16 device_serial_num;
364 if (!GetObjectUniqueId(device.get(), WPD_DEVICE_OBJECT_ID,
365 &device_serial_num)) {
366 return false;
367 }
368
369 for (size_t index = 0; index < storage_obj_ids.size(); ++index) {
Peter Kasting 2012/10/25 05:23:24 Nit: Consider using a const_iterator instead
kmadhusu 2012/10/26 02:01:24 Done.
370 // Get the storage object persistent id (E.g.: SID-{10001,D,31080448}).
371 string16 storage_unique_id;
372 if (!GetObjectUniqueId(device.get(), storage_obj_ids[index],
373 &storage_unique_id)) {
374 continue;
375 }
376 std::string device_storage_id;
377 if (GetDeviceStorageUniqueId(device_serial_num, storage_unique_id,
378 &device_storage_id)) {
379 PortableDeviceWatcherWin::DeviceStorageInfo storage_info;
380 storage_info.storage_object_id = storage_obj_ids[index];
381 storage_info.unique_id = device_storage_id;
382 storage_info_list->push_back(storage_info);
383 }
384 }
385 return true;
386 }
387
388 // Gets the device details (name, number of storages, etc.,) on the blocking
Peter Kasting 2012/10/25 05:23:24 Nit: storages
kmadhusu 2012/10/26 02:01:24 Fixed.
389 // thread. |pnp_device_id| specifies the plug and play device ID string.
390 PortableDeviceWatcherWin::DeviceDetails GetDeviceInfoOnBlockingThread(
391 IPortableDeviceManager* portable_device_manager,
392 const string16& pnp_device_id) {
393 DCHECK(content::BrowserThread::
394 GetBlockingPool()->RunsTasksOnCurrentThread());
395 DCHECK(!pnp_device_id.empty());
396 PortableDeviceWatcherWin::DeviceDetails device_details;
397 string16 device_name = GetDeviceNameOnBlockingThread(portable_device_manager,
398 pnp_device_id);
399 std::vector<PortableDeviceWatcherWin::DeviceStorageInfo> storage_info_list;
400 if (!GetDeviceStorageInfoListOnBlockingThread(pnp_device_id,
401 &storage_info_list)) {
402 return device_details;
403 }
404
405 if (IsVolumeMountedPortableDevice(device_name))
406 return device_details;
407
408 device_details.storage_info_list = storage_info_list;
409 device_details.name = device_name;
410 device_details.location = pnp_device_id;
Peter Kasting 2012/10/25 05:23:24 Nit: Seems like we could set these directly (inste
kmadhusu 2012/10/26 02:01:24 Done.
411 return device_details;
412 }
413
414 // Enumerates and returns a list of attached mtp device details on a
415 // blocking thread.
416 std::vector<PortableDeviceWatcherWin::DeviceDetails>
417 EnumerateAttachedStoragesOnBlockingThread() {
Peter Kasting 2012/10/25 05:23:24 Nit: Indent 4. Also: storages
kmadhusu 2012/10/26 02:01:24 Done.
418 DCHECK(content::BrowserThread::
419 GetBlockingPool()->RunsTasksOnCurrentThread());
420 std::vector<PortableDeviceWatcherWin::DeviceDetails> device_info_list;
421 base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr;
422 HRESULT hr = portable_device_mgr.CreateInstance(
423 __uuidof(PortableDeviceManager), NULL, CLSCTX_INPROC_SERVER);
424 if (FAILED(hr)) {
425 // Either there is no portable device support (it must be a XP with
426 // Windows Media Player Version < 10) or the thread does not have COM
427 // initialized.
428 DCHECK_NE(CO_E_NOTINITIALIZED, hr);
429 return device_info_list;
430 }
431
432 // Get the total number of devices found on the system.
433 DWORD pnp_device_count = 0;
434 hr = portable_device_mgr->GetDevices(NULL, &pnp_device_count);
435 if (FAILED(hr))
436 return device_info_list;
437
438 scoped_array<LPWSTR> pnp_device_ids(new LPWSTR[pnp_device_count]);
439 ZeroMemory(pnp_device_ids.get(), pnp_device_count);
Peter Kasting 2012/10/25 05:23:24 Doesn't operator new zero this memory for you?
kmadhusu 2012/10/26 02:01:24 No.
Peter Kasting 2012/10/26 02:14:49 Oh hey, while I'm here, the style guide says to us
kmadhusu 2012/10/26 22:23:26 Done. Used char16* instead of LPWSTR.
440 hr = portable_device_mgr->GetDevices(pnp_device_ids.get(), &pnp_device_count);
441 if (FAILED(hr))
442 return device_info_list;
443
444 for (DWORD index = 0; index < pnp_device_count; ++index) {
445 device_info_list.push_back(GetDeviceInfoOnBlockingThread(
Peter Kasting 2012/10/25 05:23:24 If GetDeviceInfoOnBlockingThread fails, it returns
kmadhusu 2012/10/26 02:01:24 Done.
446 portable_device_mgr, pnp_device_ids[index]));
447 }
448 return device_info_list;
449 }
450
451 // Handles the device attach event message on the blocking thread.
452 // |pnp_device_id| specifies the attached plug and play device ID string.
453 PortableDeviceWatcherWin::DeviceDetails
454 HandleDeviceAttachedEventOnBlockingThread(const string16& pnp_device_id) {
Peter Kasting 2012/10/25 05:23:24 Nit: Indent 4
kmadhusu 2012/10/26 02:01:24 Done.
455 DCHECK(content::BrowserThread::
456 GetBlockingPool()->RunsTasksOnCurrentThread());
457 PortableDeviceWatcherWin::DeviceDetails device_details;
458 base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr;
Peter Kasting 2012/10/25 05:23:24 Nit: This whole block that gets the portable devic
kmadhusu 2012/10/26 02:01:24 Done.
459 HRESULT hr = portable_device_mgr.CreateInstance(
460 __uuidof(PortableDeviceManager), NULL, CLSCTX_INPROC_SERVER);
461 if (FAILED(hr)) {
462 // Either there is no portable device support (it must be a XP with
463 // Windows Media Player Version < 10) or the thread does not have COM
464 // initialized.
465 DCHECK_NE(CO_E_NOTINITIALIZED, hr);
466 return device_details;
467 }
468 portable_device_mgr->RefreshDeviceList();
Peter Kasting 2012/10/25 05:23:24 Do we need to do this call in the function above t
kmadhusu 2012/10/26 02:01:24 No. It is not required there. When a new device is
Peter Kasting 2012/10/26 02:14:49 Please comment about why this is here, then, espec
kmadhusu 2012/10/26 22:23:26 Done.
469 return GetDeviceInfoOnBlockingThread(portable_device_mgr, pnp_device_id);
470 }
471
472 // Constructs and returns a storage path from storage unique identifier.
473 string16 GetStoragePathFromStorageId(const std::string& storage_unique_id) {
474 // Construct a dummy device path using the storage name. This is only used
475 // for registering device media file system.
Peter Kasting 2012/10/25 05:23:24 Nit: device -> the device
kmadhusu 2012/10/26 02:01:24 Done.
476 DCHECK(!storage_unique_id.empty());
477 std::string root_path("\\\\");
478 return UTF8ToUTF16(root_path + storage_unique_id);
479 }
480
481 } // namespace
482
483 PortableDeviceWatcherWin::PortableDeviceWatcherWin()
Peter Kasting 2012/10/25 05:23:24 Nit: Please add divider comments in this file to s
kmadhusu 2012/10/26 02:01:24 Done.
484 : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
485 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
486 content::NotificationService::AllSources());
487 }
488
489 PortableDeviceWatcherWin::~PortableDeviceWatcherWin() {
490 }
491
492 void PortableDeviceWatcherWin::Init() {
493 if (app_terminating_flag_.IsSet())
494 return;
495
496 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
Peter Kasting 2012/10/25 05:23:24 Nit: This DCHECK goes atop this function.
kmadhusu 2012/10/26 02:01:24 Done.
497 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
498 base::SequencedWorkerPool::SequenceToken media_sequence_token =
499 pool->GetNamedSequenceToken(kMediaTaskRunnerName);
500 media_task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior(
501 media_sequence_token, base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
502 DCHECK(media_task_runner_.get());
Peter Kasting 2012/10/25 05:23:24 Nit: Do we need this DCHECK? Is there some reason
kmadhusu 2012/10/26 02:01:24 Removed. We can skip this check. If it failed for
503 EnumerateAttachedDevices();
504 }
505
506 bool PortableDeviceWatcherWin::GetDeviceInfo(const FilePath& device_path,
507 string16* location,
508 std::string* unique_id,
509 string16* name,
510 bool* removable) {
511 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
512 NOTIMPLEMENTED();
Peter Kasting 2012/10/25 05:23:24 Nit: Is this TODO?
kmadhusu 2012/10/26 02:01:24 As of now, there is no UI to trigger this function
513 return false;
514 }
515
516 void PortableDeviceWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) {
517 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
518 if (app_terminating_flag_.IsSet())
519 return;
520
521 switch (event_type) {
Peter Kasting 2012/10/25 05:23:24 Nit: Simpler: if (IsPortableDeviceStructure(dat
kmadhusu 2012/10/26 02:01:24 Added the following code: if (!IsPortableDeviceS
522 case DBT_DEVICEARRIVAL: {
523 if (IsPortableDeviceStructure(data))
524 HandleDeviceAttachEvent(GetPnpDeviceId(data));
525 break;
526 }
527 case DBT_DEVICEREMOVECOMPLETE: {
528 if (IsPortableDeviceStructure(data))
529 HandleDeviceDetachEvent(GetPnpDeviceId(data));
530 break;
531 }
532 }
533 }
534
535 void PortableDeviceWatcherWin::EnumerateAttachedDevices() {
536 DCHECK(media_task_runner_.get());
537 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
538 if (app_terminating_flag_.IsSet())
539 return;
540
541 base::PostTaskAndReplyWithResult(
542 media_task_runner_,
543 FROM_HERE,
544 base::Bind(&EnumerateAttachedStoragesOnBlockingThread),
545 base::Bind(&PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices,
546 weak_ptr_factory_.GetWeakPtr()));
547 }
548
549 void PortableDeviceWatcherWin::HandleDeviceAttachEvent(
550 const string16& pnp_device_id) {
551 DCHECK(media_task_runner_.get());
552 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
553 if (app_terminating_flag_.IsSet())
554 return;
555
556 base::PostTaskAndReplyWithResult(
557 media_task_runner_,
558 FROM_HERE,
559 base::Bind(&HandleDeviceAttachedEventOnBlockingThread, pnp_device_id),
560 base::Bind(&PortableDeviceWatcherWin::OnHandleDeviceAttachEvent,
561 weak_ptr_factory_.GetWeakPtr()));
562 }
563
564 void PortableDeviceWatcherWin::HandleDeviceDetachEvent(
565 const string16& pnp_device_id) {
566 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
567 MtpDeviceMap::iterator device_entry = device_map_.find(pnp_device_id);
568 if (device_entry == device_map_.end())
569 return;
570
571 base::SystemMonitor* system_monitor = base::SystemMonitor::Get();
572 DCHECK(system_monitor);
573
574 StorageInfoList storage_info_list = device_entry->second;
575 for (size_t index = 0; index < storage_info_list.size(); ++index) {
Peter Kasting 2012/10/25 05:23:24 Nit: For looping over any typedefed type, use a [c
kmadhusu 2012/10/26 02:01:24 Done.
576 std::string storage_id = storage_info_list[index].unique_id;
577 MtpStorageMap::iterator storage_entry = storage_map_.find(storage_id);
578 DCHECK(storage_entry != storage_map_.end());
579 system_monitor->ProcessRemovableStorageDetached(
580 storage_entry->second.device_id);
581 storage_map_.erase(storage_entry);
582 }
583 device_map_.erase(device_entry);
584 }
585
586 void PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices(
587 std::vector<DeviceDetails> device_details_list) {
588 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
589 for (size_t index = 0; index < device_details_list.size(); ++index)
590 OnHandleDeviceAttachEvent(device_details_list[index]);
591 }
592
593 void PortableDeviceWatcherWin::OnHandleDeviceAttachEvent(
Peter Kasting 2012/10/25 05:23:24 Nit: Maybe this should be named OnDidHandleDeviceA
kmadhusu 2012/10/26 02:01:24 Done.
594 DeviceDetails device_details) {
595 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
596 if (app_terminating_flag_.IsSet())
597 return;
598
599 const StorageInfoList& storage_info_list = device_details.storage_info_list;
600 const string16& name = device_details.name;
601 const string16& location = device_details.location;
602 DCHECK(!ContainsKey(device_map_, location));
603 base::SystemMonitor* system_monitor = base::SystemMonitor::Get();
604 DCHECK(system_monitor);
605 for (size_t index = 0; index < storage_info_list.size(); ++index) {
606 const std::string& storage_id = storage_info_list[index].unique_id;
607 DCHECK(!ContainsKey(storage_map_, storage_id));
608
609 // Keep track of storage id and storage name to see how often we receive
610 // empty values.
611 MediaStorageUtil::RecordDeviceInfoHistogram(false, storage_id, name);
612 if (storage_id.empty() || name.empty())
613 return;
614
615 // Device can have several data storage_info_list. Therefore, add the
Peter Kasting 2012/10/25 05:23:24 Nit: This first sentence seems to be missing some
kmadhusu 2012/10/26 02:01:24 Fixed.
616 // partition details to the storage name. E.g.: "Nexus 7 (s10001)"
617 string16 storage_name = base::StringPrintf(
618 L"%ls (%ls)", name.c_str(),
619 storage_info_list[index].storage_object_id.c_str());
620
621 base::SystemMonitor::RemovableStorageInfo storage_info(storage_id,
Peter Kasting 2012/10/25 05:23:24 Nit: Or just inline this into the next statement
kmadhusu 2012/10/26 02:01:24 Done
622 storage_name,
623 location);
624 storage_map_[storage_id] = storage_info;
625 system_monitor->ProcessRemovableStorageAttached(
626 storage_id, storage_name, GetStoragePathFromStorageId(storage_id));
627 }
628 device_map_[location] = storage_info_list;
629 }
630
631 void PortableDeviceWatcherWin::Observe(
632 int type,
633 const content::NotificationSource& source,
634 const content::NotificationDetails& details) {
635 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
636 DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type);
637 app_terminating_flag_.Set();
638 }
639
640 } // namespace chrome
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698