Chromium Code Reviews| 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/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 "content/public/browser/browser_thread.h" | |
| 21 | |
| 22 namespace chrome { | |
| 23 | |
| 24 namespace { | |
| 25 | |
| 26 // Name of the client application that communicates with the mtp device. | |
| 27 const char16 kClientName[] = L"Chromium"; | |
| 28 | |
| 29 // Name of the sequenced task runner. | |
| 30 const char kMediaTaskRunnerName[] = "media-task-runner"; | |
| 31 | |
| 32 // Returns true if |data| represents a class of portable devices. | |
| 33 bool IsPortableDeviceStructure(LPARAM data) { | |
| 34 DEV_BROADCAST_HDR* broadcast_hdr = | |
| 35 reinterpret_cast<DEV_BROADCAST_HDR*>(data); | |
| 36 if (!broadcast_hdr || | |
| 37 (broadcast_hdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)) { | |
| 38 return false; | |
| 39 } | |
| 40 | |
| 41 GUID guidDevInterface = GUID_NULL; | |
| 42 if (FAILED(CLSIDFromString(kWPDDevInterfaceGUID, &guidDevInterface))) | |
| 43 return false; | |
| 44 DEV_BROADCAST_DEVICEINTERFACE* dev_interface = | |
| 45 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data); | |
| 46 return (IsEqualGUID(dev_interface->dbcc_classguid, guidDevInterface) != 0); | |
| 47 } | |
| 48 | |
| 49 // Returns the portable device plug and play device ID string. | |
| 50 string16 GetPnpDeviceId(LPARAM data) { | |
| 51 DEV_BROADCAST_DEVICEINTERFACE* dev_interface = | |
| 52 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data); | |
| 53 if (!dev_interface) | |
| 54 return string16(); | |
| 55 string16 device_id(dev_interface->dbcc_name); | |
| 56 DCHECK(IsStringASCII(device_id)); | |
| 57 return StringToLowerASCII(device_id); | |
| 58 } | |
| 59 | |
| 60 // Gets the friendly name of the device specified by the |pnp_device_id|. On | |
| 61 // success, returns true and fills in |name|. | |
| 62 bool GetFriendlyName(const string16& pnp_device_id, | |
| 63 IPortableDeviceManager* device_manager, | |
| 64 string16* name) { | |
| 65 DCHECK(device_manager); | |
| 66 DCHECK(name); | |
| 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 hr = device_manager->GetDeviceFriendlyName( | |
| 74 pnp_device_id.c_str(), WriteInto(name, name_len), &name_len); | |
| 75 return SUCCEEDED(hr) ? !name->empty() : false; | |
|
Peter Kasting
2012/10/26 23:46:23
Nit: Simpler:
return SUCCEEDED(hr) && !name->em
kmadhusu
2012/10/28 22:57:16
Done.
| |
| 76 } | |
| 77 | |
| 78 // Gets the manufacturer name of the device specified by the |pnp_device_id|. | |
| 79 // On success, returns true and fills in |name|. | |
| 80 bool GetManufacturerName(const string16& pnp_device_id, | |
| 81 IPortableDeviceManager* device_manager, | |
| 82 string16* name) { | |
| 83 DCHECK(device_manager); | |
| 84 DCHECK(name); | |
| 85 DWORD name_len = 0; | |
| 86 HRESULT hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(), | |
| 87 NULL, &name_len); | |
| 88 if (FAILED(hr)) | |
| 89 return false; | |
| 90 | |
| 91 hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(), | |
| 92 WriteInto(name, name_len), | |
| 93 &name_len); | |
| 94 return SUCCEEDED(hr) ? !name->empty() : false; | |
| 95 } | |
| 96 | |
| 97 // Gets the description of the device specified by the |pnp_device_id|. On | |
| 98 // success, returns true and fills in |description|. | |
| 99 bool GetDeviceDescription(const string16& pnp_device_id, | |
| 100 IPortableDeviceManager* device_manager, | |
| 101 string16* description) { | |
| 102 DCHECK(device_manager); | |
| 103 DCHECK(description); | |
| 104 DWORD desc_len = 0; | |
| 105 HRESULT hr = device_manager->GetDeviceDescription(pnp_device_id.c_str(), NULL, | |
| 106 &desc_len); | |
| 107 if (FAILED(hr)) | |
| 108 return false; | |
| 109 | |
| 110 hr = device_manager->GetDeviceDescription(pnp_device_id.c_str(), | |
| 111 WriteInto(description, desc_len), | |
| 112 &desc_len); | |
| 113 return SUCCEEDED(hr) ? !description->empty() : false; | |
| 114 } | |
| 115 | |
| 116 // On success, returns true and updates |client_info| with a reference to an | |
| 117 // IPortableDeviceValues interface that holds information about the | |
| 118 // application that communicates with the device. | |
| 119 bool GetClientInformation( | |
| 120 base::win::ScopedComPtr<IPortableDeviceValues>* client_info) { | |
| 121 HRESULT hr = client_info->CreateInstance(__uuidof(PortableDeviceValues), | |
| 122 NULL, CLSCTX_INPROC_SERVER); | |
| 123 if (FAILED(hr)) { | |
| 124 DPLOG(ERROR) << "Failed to create an instance of IPortableDeviceValues"; | |
| 125 return false; | |
| 126 } | |
| 127 | |
| 128 // Attempt to set client details. | |
| 129 (*client_info)->SetStringValue(WPD_CLIENT_NAME, kClientName); | |
| 130 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, 0); | |
| 131 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, 0); | |
| 132 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_REVISION, 0); | |
| 133 (*client_info)->SetUnsignedIntegerValue( | |
| 134 WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE, SECURITY_IMPERSONATION); | |
| 135 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS, | |
| 136 GENERIC_READ); | |
| 137 return true; | |
| 138 } | |
| 139 | |
| 140 // Opens the device for communication. |pnp_device_id| specifies the plug and | |
| 141 // play device ID string. On success, returns true and updates |device| with a | |
| 142 // reference to the portable device interface. | |
| 143 bool SetUp(const string16& pnp_device_id, | |
| 144 base::win::ScopedComPtr<IPortableDevice>* device) { | |
| 145 base::win::ScopedComPtr<IPortableDeviceValues> client_info; | |
| 146 if (!GetClientInformation(&client_info)) | |
| 147 return false; | |
| 148 | |
| 149 HRESULT hr = device->CreateInstance(__uuidof(PortableDevice), NULL, | |
| 150 CLSCTX_INPROC_SERVER); | |
| 151 if (FAILED(hr)) { | |
| 152 DPLOG(ERROR) << "Failed to create an instance of IPortableDevice"; | |
| 153 return false; | |
| 154 } | |
| 155 | |
| 156 hr = (*device)->Open(pnp_device_id.c_str(), client_info.get()); | |
| 157 if (SUCCEEDED(hr)) | |
| 158 return true; | |
| 159 | |
| 160 if (hr == E_ACCESSDENIED) | |
| 161 DPLOG(ERROR) << "Access denied to open the device"; | |
| 162 return false; | |
| 163 } | |
| 164 | |
| 165 // Returns the unique id property key of the object specified by the | |
| 166 // |object_id|. | |
| 167 REFPROPERTYKEY GetUniqueIdPropertyKey(const string16& object_id) { | |
| 168 return (object_id == WPD_DEVICE_OBJECT_ID) ? | |
| 169 WPD_DEVICE_SERIAL_NUMBER : WPD_OBJECT_PERSISTENT_UNIQUE_ID; | |
| 170 } | |
| 171 | |
| 172 // On success, returns true and populates |properties_to_read| with the | |
| 173 // property key of the object specified by the |object_id|. | |
| 174 bool PopulatePropertyKeyCollection( | |
| 175 const string16& object_id, | |
| 176 base::win::ScopedComPtr<IPortableDeviceKeyCollection>* properties_to_read) { | |
| 177 HRESULT hr = properties_to_read->CreateInstance( | |
| 178 __uuidof(PortableDeviceKeyCollection), NULL, CLSCTX_INPROC_SERVER); | |
| 179 if (FAILED(hr)) { | |
| 180 DPLOG(ERROR) << "Failed to create IPortableDeviceKeyCollection instance"; | |
| 181 return false; | |
| 182 } | |
| 183 REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id); | |
| 184 hr = (*properties_to_read)->Add(key); | |
| 185 return SUCCEEDED(hr); | |
| 186 } | |
| 187 | |
| 188 // Wrapper function to get content property string value. | |
| 189 bool GetStringPropertyValue(IPortableDeviceValues* properties_values, | |
| 190 REFPROPERTYKEY key, | |
| 191 string16* value) { | |
| 192 DCHECK(properties_values); | |
| 193 DCHECK(value); | |
| 194 base::win::ScopedCoMem<char16> buffer; | |
| 195 HRESULT hr = properties_values->GetStringValue(key, &buffer); | |
| 196 if (FAILED(hr)) | |
| 197 return false; | |
| 198 *value = static_cast<const wchar_t*>(buffer); | |
|
Peter Kasting
2012/10/26 23:46:23
Nit: wchar_t -> char16, since the original type an
kmadhusu
2012/10/28 22:57:16
Done.
| |
| 199 return true; | |
| 200 } | |
| 201 | |
| 202 // Constructs a unique identifier for the object specified by the |object_id|. | |
| 203 // On success, returns true and fills in |unique_id|. | |
| 204 bool GetObjectUniqueId(IPortableDevice* device, | |
| 205 const string16& object_id, | |
| 206 string16* unique_id) { | |
| 207 DCHECK(device); | |
| 208 DCHECK(unique_id); | |
| 209 base::win::ScopedComPtr<IPortableDeviceContent> content; | |
| 210 HRESULT hr = device->Content(content.Receive()); | |
| 211 if (FAILED(hr)) { | |
| 212 DPLOG(ERROR) << "Failed to get IPortableDeviceContent interface"; | |
| 213 return false; | |
| 214 } | |
| 215 | |
| 216 base::win::ScopedComPtr<IPortableDeviceProperties> properties; | |
| 217 hr = content->Properties(properties.Receive()); | |
| 218 if (FAILED(hr)) { | |
| 219 DPLOG(ERROR) << "Failed to get IPortableDeviceProperties interface"; | |
| 220 return false; | |
| 221 } | |
| 222 | |
| 223 base::win::ScopedComPtr<IPortableDeviceKeyCollection> properties_to_read; | |
| 224 if (!PopulatePropertyKeyCollection(object_id, &properties_to_read)) | |
| 225 return false; | |
| 226 | |
| 227 base::win::ScopedComPtr<IPortableDeviceValues> properties_values; | |
| 228 if (FAILED(properties->GetValues(object_id.c_str(), | |
| 229 properties_to_read.get(), | |
| 230 properties_values.Receive()))) { | |
| 231 return false; | |
| 232 } | |
| 233 | |
| 234 REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id); | |
| 235 return GetStringPropertyValue(properties_values.get(), key, unique_id); | |
| 236 } | |
| 237 | |
| 238 // Constructs the device storage unique identifier using |device_serial_num| and | |
| 239 // |storage_id|. On success, returns true and fills in |device_storage_id|. | |
| 240 bool ConstructDeviceStorageUniqueId(const string16& device_serial_num, | |
| 241 const string16& storage_id, | |
| 242 std::string* device_storage_id) { | |
| 243 if (device_serial_num.empty() && storage_id.empty()) | |
| 244 return false; | |
| 245 | |
| 246 DCHECK(device_storage_id); | |
| 247 string16 unique_id(storage_id + L':' + device_serial_num); | |
| 248 | |
| 249 *device_storage_id = MediaStorageUtil::MakeDeviceId( | |
| 250 MediaStorageUtil::MTP_OR_PTP, UTF16ToUTF8(unique_id)); | |
| 251 return true; | |
| 252 } | |
| 253 | |
| 254 // Gets a list of removable storage object identifiers present in |device|. | |
| 255 // On success, returns true and fills in |storage_ids|. | |
| 256 bool GetRemovableStorageObjectIds(IPortableDevice* device, | |
| 257 std::vector<string16>* storage_ids) { | |
| 258 DCHECK(device); | |
| 259 DCHECK(storage_ids); | |
| 260 base::win::ScopedComPtr<IPortableDeviceCapabilities> capabilities; | |
| 261 HRESULT hr = device->Capabilities(capabilities.Receive()); | |
| 262 if (FAILED(hr)) { | |
| 263 DPLOG(ERROR) << "Failed to get IPortableDeviceCapabilities interface"; | |
| 264 return false; | |
| 265 } | |
| 266 | |
| 267 base::win::ScopedComPtr<IPortableDevicePropVariantCollection> storage_obj_ids; | |
| 268 hr = capabilities->GetFunctionalObjects(WPD_FUNCTIONAL_CATEGORY_STORAGE, | |
| 269 storage_obj_ids.Receive()); | |
| 270 if (FAILED(hr)) { | |
| 271 DPLOG(ERROR) << "Failed to get IPortableDevicePropVariantCollection"; | |
| 272 return false; | |
| 273 } | |
| 274 | |
| 275 DWORD num_storage_obj_ids = 0; | |
| 276 hr = storage_obj_ids->GetCount(&num_storage_obj_ids); | |
| 277 if (FAILED(hr)) | |
| 278 return false; | |
| 279 | |
| 280 for (DWORD index = 0; index < num_storage_obj_ids; ++index) { | |
| 281 PROPVARIANT object_id = {0}; | |
| 282 PropVariantInit(&object_id); | |
| 283 hr = storage_obj_ids->GetAt(index, &object_id); | |
| 284 if (SUCCEEDED(hr) && (object_id.pwszVal != NULL) && | |
| 285 (object_id.vt == VT_LPWSTR)) { | |
| 286 storage_ids->push_back(object_id.pwszVal); | |
| 287 } | |
| 288 PropVariantClear(&object_id); | |
| 289 } | |
| 290 return true; | |
| 291 } | |
| 292 | |
| 293 // Returns true if the portable device is mounted on a volume. |device_name| | |
| 294 // specifies the name of the device. | |
| 295 bool IsVolumeMountedPortableDevice(const string16& device_name) { | |
| 296 // If the device is a volume mounted device, |device_name| will be | |
| 297 // the volume name. | |
| 298 return (device_name.length() >= 2) && (device_name[1] == L':') && | |
| 299 ((device_name[0] >= L'A') && (device_name[0] <= L'Z') || | |
|
Peter Kasting
2012/10/26 23:46:23
Nit: Should be indented 4.
Also, add parens: righ
kmadhusu
2012/10/28 22:57:16
I thought we align the conditions. I am not sure a
Peter Kasting
2012/10/29 20:41:33
Huh, somehow in the "diff against prior CL" view i
| |
| 300 ((device_name[0] >= L'a') && (device_name[0] <= L'z'))); | |
| 301 } | |
| 302 | |
| 303 // Returns the name of the device specified by |pnp_device_id|. | |
| 304 // Since this task may take a longer time to complete, this function is | |
| 305 // accessed via the media task runner. | |
| 306 string16 GetDeviceNameOnBlockingThread( | |
| 307 IPortableDeviceManager* portable_device_manager, | |
| 308 const string16& pnp_device_id) { | |
| 309 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 310 DCHECK(portable_device_manager); | |
| 311 string16 name; | |
| 312 GetFriendlyName(pnp_device_id, portable_device_manager, &name) || | |
| 313 GetDeviceDescription(pnp_device_id, portable_device_manager, &name) || | |
| 314 GetManufacturerName(pnp_device_id, portable_device_manager, &name); | |
| 315 return name; | |
| 316 } | |
| 317 | |
| 318 // Access the device and gets the device storage details. Since this task may | |
| 319 // take a longer time to complete, this function is accessed via the media task | |
| 320 // runner. On success, returns true and populates |storage_objects| with device | |
|
Peter Kasting
2012/10/26 23:46:23
Nit: You added comments to a number of functions s
kmadhusu
2012/10/28 22:57:16
I misunderstood your earlier comment.
How about
Peter Kasting
2012/10/29 20:41:33
I don't want a block of text that's pasted into se
kmadhusu
2012/10/30 01:29:24
Removed the redundant (on why I am running these t
| |
| 321 // storage details. | |
| 322 bool GetDeviceStorageObjectsOnBlockingThread( | |
| 323 const string16& pnp_device_id, | |
| 324 PortableDeviceWatcherWin::StorageObjects* storage_objects) { | |
| 325 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 326 DCHECK(storage_objects); | |
| 327 base::win::ScopedComPtr<IPortableDevice> device; | |
| 328 if (!SetUp(pnp_device_id, &device)) | |
| 329 return false; | |
| 330 | |
| 331 string16 device_serial_num; | |
| 332 if (!GetObjectUniqueId(device.get(), WPD_DEVICE_OBJECT_ID, | |
| 333 &device_serial_num)) { | |
| 334 return false; | |
| 335 } | |
| 336 | |
| 337 std::vector<string16> storage_obj_ids; | |
| 338 if (!GetRemovableStorageObjectIds(device.get(), &storage_obj_ids)) | |
| 339 return false; | |
| 340 | |
| 341 for (std::vector<string16>::const_iterator id_iter = storage_obj_ids.begin(); | |
|
Peter Kasting
2012/10/26 23:46:23
Nit: You are inconsistent across this file about h
kmadhusu
2012/10/28 22:57:16
"i" or "it" does not provide any information about
| |
| 342 id_iter != storage_obj_ids.end(); ++id_iter) { | |
| 343 string16 storage_persistent_id; | |
| 344 if (!GetObjectUniqueId(device.get(), *id_iter, &storage_persistent_id)) | |
| 345 continue; | |
| 346 | |
| 347 std::string device_storage_id; | |
| 348 if (ConstructDeviceStorageUniqueId(device_serial_num, storage_persistent_id, | |
| 349 &device_storage_id)) { | |
| 350 PortableDeviceWatcherWin::DeviceStorageObject storage_object; | |
|
Peter Kasting
2012/10/26 23:46:23
Nit: Use aggregate initialization form or add a co
kmadhusu
2012/10/28 22:57:16
Added a constructor.
| |
| 351 storage_object.object_temporary_id = *id_iter; | |
| 352 storage_object.object_persistent_id = device_storage_id; | |
| 353 storage_objects->push_back(storage_object); | |
| 354 } | |
| 355 } | |
| 356 return true; | |
| 357 } | |
| 358 | |
| 359 // Access the device and gets the device details (name, storage info, etc.,). | |
|
Peter Kasting
2012/10/26 23:46:23
Nit: "Access" -> "Accesses", "etc.," -> "etc."
kmadhusu
2012/10/28 22:57:16
Done.
| |
| 360 // Since this task may take a longer time to complete, this function is | |
| 361 // accessed via the media task runner. On success returns true and fills in | |
| 362 // |device_details|. |pnp_device_id| specifies the plug and play device ID | |
| 363 // string. | |
| 364 bool GetDeviceInfoOnBlockingThread( | |
| 365 IPortableDeviceManager* portable_device_manager, | |
| 366 const string16& pnp_device_id, | |
| 367 PortableDeviceWatcherWin::DeviceDetails* device_details) { | |
| 368 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 369 DCHECK(portable_device_manager); | |
| 370 DCHECK(device_details); | |
| 371 DCHECK(!pnp_device_id.empty()); | |
| 372 | |
| 373 device_details->name = GetDeviceNameOnBlockingThread(portable_device_manager, | |
| 374 pnp_device_id); | |
| 375 if (IsVolumeMountedPortableDevice(device_details->name)) | |
| 376 return false; | |
| 377 | |
| 378 device_details->location = pnp_device_id; | |
| 379 PortableDeviceWatcherWin::StorageObjects storage_objects; | |
| 380 return GetDeviceStorageObjectsOnBlockingThread( | |
| 381 pnp_device_id, &device_details->storage_objects); | |
| 382 } | |
| 383 | |
| 384 // Wrapper function to get an instance of portable device manager. This function | |
| 385 // may take a long time to complete. Therefore, access this function via a media | |
| 386 // task runner. On success, returns true and fills in |portable_device_mgr|. | |
| 387 bool GetPortableDeviceManager( | |
| 388 base::win::ScopedComPtr<IPortableDeviceManager>* portable_device_mgr) { | |
| 389 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 390 HRESULT hr = portable_device_mgr->CreateInstance( | |
| 391 __uuidof(PortableDeviceManager), NULL, CLSCTX_INPROC_SERVER); | |
| 392 if (SUCCEEDED(hr)) | |
| 393 return true; | |
| 394 | |
| 395 // Either there is no portable device support (it must be a XP with Windows | |
|
Peter Kasting
2012/10/26 23:46:23
Nit: "it must be a XP with Windows Media Player Ve
kmadhusu
2012/10/28 22:57:16
Done.
| |
| 396 // Media Player Version < 10) or the thread does not have COM initialized. | |
| 397 DCHECK_NE(CO_E_NOTINITIALIZED, hr); | |
| 398 return false; | |
| 399 } | |
| 400 | |
| 401 // Enumerates the attached portable devices. This function accesses the device | |
| 402 // to get the device details. Since this task may take a longer time to | |
| 403 // complete, this function is accessed via the media task runner. On success, | |
| 404 // fills in |device_details_vector| with the attached portable device details. | |
| 405 void EnumerateAttachedDevicesOnBlockingThread( | |
| 406 PortableDeviceWatcherWin::DeviceDetailsVector* device_details_vector) { | |
| 407 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 408 DCHECK(device_details_vector); | |
| 409 base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr; | |
| 410 if (!GetPortableDeviceManager(&portable_device_mgr)) | |
| 411 return; | |
| 412 | |
| 413 // Get the total number of devices found on the system. | |
| 414 DWORD pnp_device_count = 0; | |
| 415 HRESULT hr = portable_device_mgr->GetDevices(NULL, &pnp_device_count); | |
| 416 if (FAILED(hr)) | |
| 417 return; | |
| 418 | |
| 419 scoped_array<char16*> pnp_device_ids(new char16*[pnp_device_count]); | |
| 420 ZeroMemory(pnp_device_ids.get(), pnp_device_count); | |
| 421 hr = portable_device_mgr->GetDevices(pnp_device_ids.get(), &pnp_device_count); | |
| 422 if (FAILED(hr)) | |
| 423 return; | |
| 424 | |
| 425 for (DWORD index = 0; index < pnp_device_count; ++index) { | |
| 426 PortableDeviceWatcherWin::DeviceDetails device_details; | |
| 427 if (!GetDeviceInfoOnBlockingThread(portable_device_mgr, | |
| 428 pnp_device_ids[index], | |
| 429 &device_details)) { | |
| 430 continue; | |
| 431 } | |
| 432 device_details_vector->push_back(device_details); | |
| 433 } | |
| 434 } | |
| 435 | |
| 436 // Handles the device attach event message on a media task runner. This | |
| 437 // function communicates with the device to get the storage details. This task | |
| 438 // may take a longer time. Therefore, access this function on a media task | |
| 439 // runner. |pnp_device_id| specifies the attached plug and play device ID | |
| 440 // string. On success, populates |device_details| with device information. | |
| 441 void HandleDeviceAttachedEventOnBlockingThread( | |
| 442 const string16& pnp_device_id, | |
| 443 PortableDeviceWatcherWin::DeviceDetails* device_details) { | |
| 444 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 445 DCHECK(device_details); | |
| 446 base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr; | |
| 447 if (!GetPortableDeviceManager(&portable_device_mgr)) | |
| 448 return; | |
| 449 // Sometimes, portable device manager doesn't have the new device details. | |
| 450 // Refresh the manager device list to update its details. | |
| 451 portable_device_mgr->RefreshDeviceList(); | |
| 452 GetDeviceInfoOnBlockingThread(portable_device_mgr, pnp_device_id, | |
| 453 device_details); | |
| 454 } | |
| 455 | |
| 456 // Constructs and returns a storage path from storage unique identifier. | |
| 457 string16 GetStoragePathFromStorageId(const std::string& storage_unique_id) { | |
| 458 // Construct a dummy device path using the storage name. This is only used | |
| 459 // for registering the device media file system. | |
| 460 DCHECK(!storage_unique_id.empty()); | |
| 461 const std::string root_path("\\\\"); | |
|
Peter Kasting
2012/10/26 23:46:23
Nit: Frankly I think this should just be inlined i
kmadhusu
2012/10/28 22:57:16
Done.
| |
| 462 return UTF8ToUTF16(root_path + storage_unique_id); | |
| 463 } | |
| 464 | |
| 465 } // namespace | |
| 466 | |
| 467 | |
| 468 /////////////////////////////////////////////////////////////////////////////// | |
| 469 // PortableDeviceWatcherWin implementation // | |
| 470 /////////////////////////////////////////////////////////////////////////////// | |
|
Peter Kasting
2012/10/26 23:46:23
Nit: More common is the following:
...
} // name
kmadhusu
2012/10/28 22:57:16
Done.
| |
| 471 PortableDeviceWatcherWin::PortableDeviceWatcherWin() | |
| 472 : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | |
| 473 } | |
| 474 | |
| 475 PortableDeviceWatcherWin::~PortableDeviceWatcherWin() { | |
| 476 } | |
| 477 | |
| 478 void PortableDeviceWatcherWin::Init() { | |
| 479 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 480 | |
| 481 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); | |
| 482 base::SequencedWorkerPool::SequenceToken media_sequence_token = | |
| 483 pool->GetNamedSequenceToken(kMediaTaskRunnerName); | |
| 484 media_task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior( | |
| 485 media_sequence_token, base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); | |
| 486 EnumerateAttachedDevices(); | |
| 487 } | |
| 488 | |
| 489 void PortableDeviceWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) { | |
| 490 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 491 | |
| 492 if (!IsPortableDeviceStructure(data)) | |
| 493 return; | |
| 494 | |
| 495 string16 device_id = GetPnpDeviceId(data); | |
| 496 if (event_type == DBT_DEVICEARRIVAL) | |
| 497 HandleDeviceAttachEvent(device_id); | |
| 498 else if (event_type == DBT_DEVICEREMOVECOMPLETE) | |
| 499 HandleDeviceDetachEvent(device_id); | |
| 500 } | |
| 501 | |
| 502 void PortableDeviceWatcherWin::EnumerateAttachedDevices() { | |
| 503 DCHECK(media_task_runner_.get()); | |
| 504 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 505 | |
| 506 DeviceDetailsVector* device_details_vector = new DeviceDetailsVector; | |
| 507 media_task_runner_->PostTaskAndReply( | |
| 508 FROM_HERE, | |
| 509 base::Bind(&EnumerateAttachedDevicesOnBlockingThread, | |
| 510 device_details_vector), | |
| 511 base::Bind(&PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices, | |
| 512 weak_ptr_factory_.GetWeakPtr(), | |
| 513 base::Owned(device_details_vector))); | |
| 514 } | |
| 515 | |
| 516 void PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices( | |
| 517 const DeviceDetailsVector* device_details_vector) { | |
| 518 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 519 DCHECK(device_details_vector); | |
| 520 for (DeviceDetailsVector::const_iterator it = device_details_vector->begin(); | |
| 521 it != device_details_vector->end(); ++it) { | |
|
Peter Kasting
2012/10/26 23:46:23
Nit: {} unnecessary
kmadhusu
2012/10/28 22:57:16
If the condition spans over a line, I am adding {}
| |
| 522 OnDidHandleDeviceAttachEvent(&(*it)); | |
| 523 } | |
| 524 } | |
| 525 | |
| 526 void PortableDeviceWatcherWin::HandleDeviceAttachEvent( | |
| 527 const string16& pnp_device_id) { | |
| 528 DCHECK(media_task_runner_.get()); | |
| 529 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 530 | |
| 531 DeviceDetails* device_details = new DeviceDetails; | |
| 532 media_task_runner_->PostTaskAndReply( | |
| 533 FROM_HERE, | |
| 534 base::Bind(&HandleDeviceAttachedEventOnBlockingThread, pnp_device_id, | |
| 535 device_details), | |
| 536 base::Bind(&PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent, | |
| 537 weak_ptr_factory_.GetWeakPtr(), | |
| 538 base::Owned(device_details))); | |
| 539 } | |
| 540 | |
| 541 void PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent( | |
| 542 const DeviceDetails* device_details) { | |
| 543 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 544 DCHECK(device_details); | |
| 545 | |
|
Peter Kasting
2012/10/26 23:46:23
HandleDeviceAttachedEvent() can fail and leave |de
kmadhusu
2012/10/28 22:57:16
Modified code to check the return value of HandleD
| |
| 546 const StorageObjects& storage_objects = device_details->storage_objects; | |
| 547 const string16& name = device_details->name; | |
| 548 const string16& location = device_details->location; | |
|
Peter Kasting
2012/10/26 23:46:23
Nit: If any of these refs doesn't end up saving li
kmadhusu
2012/10/28 22:57:16
I would like to leave it as it is.
| |
| 549 DCHECK(!ContainsKey(device_map_, location)); | |
| 550 | |
| 551 base::SystemMonitor* system_monitor = base::SystemMonitor::Get(); | |
| 552 DCHECK(system_monitor); | |
| 553 for (StorageObjects::const_iterator storage_iter = storage_objects.begin(); | |
| 554 storage_iter != storage_objects.end(); ++storage_iter) { | |
| 555 const std::string& storage_id = storage_iter->object_persistent_id; | |
| 556 DCHECK(!ContainsKey(storage_map_, storage_id)); | |
| 557 | |
| 558 // Keep track of storage id and storage name to see how often we receive | |
| 559 // empty values. | |
| 560 MediaStorageUtil::RecordDeviceInfoHistogram(false, storage_id, name); | |
| 561 if (storage_id.empty() || name.empty()) | |
| 562 return; | |
| 563 | |
| 564 // Device can have several data partitions. Therefore, add the | |
| 565 // partition identifier to the storage name. E.g.: "Nexus 7 (s10001)" | |
| 566 string16 storage_name(name + L" (" + storage_iter->object_temporary_id + | |
| 567 L')'); | |
|
Peter Kasting
2012/10/26 23:46:23
Nit: Indent 4
kmadhusu
2012/10/28 22:57:16
Done.
| |
| 568 storage_map_[storage_id] = | |
| 569 base::SystemMonitor::RemovableStorageInfo(storage_id, storage_name, | |
| 570 location); | |
|
Peter Kasting
2012/10/26 23:46:23
Nit: Arg must be aligned with first arg above
kmadhusu
2012/10/28 22:57:16
Done.
| |
| 571 system_monitor->ProcessRemovableStorageAttached( | |
| 572 storage_id, storage_name, GetStoragePathFromStorageId(storage_id)); | |
| 573 } | |
| 574 device_map_[location] = storage_objects; | |
| 575 } | |
| 576 | |
| 577 void PortableDeviceWatcherWin::HandleDeviceDetachEvent( | |
| 578 const string16& pnp_device_id) { | |
| 579 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 580 MtpDeviceMap::iterator device_entry = device_map_.find(pnp_device_id); | |
| 581 if (device_entry == device_map_.end()) | |
| 582 return; | |
| 583 | |
| 584 base::SystemMonitor* system_monitor = base::SystemMonitor::Get(); | |
| 585 DCHECK(system_monitor); | |
| 586 | |
| 587 StorageObjects storage_objects = device_entry->second; | |
|
Peter Kasting
2012/10/26 23:46:23
Nit: Does the loop below modify or invalidate what
kmadhusu
2012/10/28 22:57:16
Loop does not modify |device_entry|->second value.
| |
| 588 for (StorageObjects::const_iterator storage_iter = storage_objects.begin(); | |
| 589 storage_iter != storage_objects.end(); ++storage_iter) { | |
| 590 std::string storage_id = storage_iter->object_persistent_id; | |
| 591 MtpStorageMap::iterator storage_entry = storage_map_.find(storage_id); | |
| 592 DCHECK(storage_entry != storage_map_.end()); | |
| 593 system_monitor->ProcessRemovableStorageDetached( | |
| 594 storage_entry->second.device_id); | |
| 595 storage_map_.erase(storage_entry); | |
| 596 } | |
| 597 device_map_.erase(device_entry); | |
| 598 } | |
| 599 | |
| 600 } // namespace chrome | |
| OLD | NEW |