OLD | NEW |
---|---|
(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 | |
OLD | NEW |