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()); | |
| 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()); | |
| 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()); | |
| 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 char16*>(buffer); | |
| 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); | |
|
Peter Kasting
2012/10/29 21:57:19
Nit: I suggest inlining this below to avoid the te
kmadhusu
2012/10/30 01:29:24
Done.
| |
| 248 | |
|
Peter Kasting
2012/10/29 21:57:19
Nit: Blank line unnecessary
kmadhusu
2012/10/30 01:29:24
Done.
| |
| 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_object_ids|. | |
| 256 bool GetRemovableStorageObjectIds( | |
| 257 IPortableDevice* device, | |
| 258 PortableDeviceWatcherWin::StorageObjectIDs* storage_object_ids) { | |
| 259 DCHECK(device); | |
| 260 DCHECK(storage_object_ids); | |
| 261 base::win::ScopedComPtr<IPortableDeviceCapabilities> capabilities; | |
| 262 HRESULT hr = device->Capabilities(capabilities.Receive()); | |
| 263 if (FAILED(hr)) { | |
| 264 DPLOG(ERROR) << "Failed to get IPortableDeviceCapabilities interface"; | |
| 265 return false; | |
| 266 } | |
| 267 | |
| 268 base::win::ScopedComPtr<IPortableDevicePropVariantCollection> storage_ids; | |
| 269 hr = capabilities->GetFunctionalObjects(WPD_FUNCTIONAL_CATEGORY_STORAGE, | |
| 270 storage_ids.Receive()); | |
| 271 if (FAILED(hr)) { | |
| 272 DPLOG(ERROR) << "Failed to get IPortableDevicePropVariantCollection"; | |
| 273 return false; | |
| 274 } | |
| 275 | |
| 276 DWORD num_storage_obj_ids = 0; | |
| 277 hr = storage_ids->GetCount(&num_storage_obj_ids); | |
| 278 if (FAILED(hr)) | |
| 279 return false; | |
| 280 | |
| 281 for (DWORD index = 0; index < num_storage_obj_ids; ++index) { | |
| 282 PROPVARIANT object_id = {0}; | |
| 283 PropVariantInit(&object_id); | |
| 284 hr = storage_ids->GetAt(index, &object_id); | |
| 285 if (SUCCEEDED(hr) && (object_id.pwszVal != NULL) && | |
| 286 (object_id.vt == VT_LPWSTR)) { | |
| 287 storage_object_ids->push_back(object_id.pwszVal); | |
| 288 } | |
| 289 PropVariantClear(&object_id); | |
| 290 } | |
| 291 return true; | |
| 292 } | |
| 293 | |
| 294 // Returns true if the portable device is mounted on a volume. |device_name| | |
| 295 // specifies the name of the device. | |
| 296 bool IsVolumeMountedPortableDevice(const string16& device_name) { | |
| 297 // If the device is a volume mounted device, |device_name| will be | |
| 298 // the volume name. | |
| 299 return ((device_name.length() >= 2) && (device_name[1] == L':') && | |
| 300 (((device_name[0] >= L'A') && (device_name[0] <= L'Z')) || | |
| 301 ((device_name[0] >= L'a') && (device_name[0] <= L'z')))); | |
| 302 } | |
| 303 | |
| 304 // Returns the name of the device specified by |pnp_device_id|. | |
| 305 // Since this task may take a longer time to complete, this function is | |
| 306 // accessed via the media task runner. | |
| 307 string16 GetDeviceNameOnBlockingThread( | |
| 308 IPortableDeviceManager* portable_device_manager, | |
| 309 const string16& pnp_device_id) { | |
| 310 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 311 DCHECK(portable_device_manager); | |
| 312 string16 name; | |
| 313 GetFriendlyName(pnp_device_id, portable_device_manager, &name) || | |
| 314 GetDeviceDescription(pnp_device_id, portable_device_manager, &name) || | |
| 315 GetManufacturerName(pnp_device_id, portable_device_manager, &name); | |
| 316 return name; | |
| 317 } | |
| 318 | |
| 319 // Access the device and gets the device storage details. Since this task may | |
| 320 // take a longer time to complete, this function is accessed via the media task | |
| 321 // runner. On success, returns true and populates |storage_objects| with device | |
| 322 // storage details. | |
| 323 bool GetDeviceStorageObjectsOnBlockingThread( | |
| 324 const string16& pnp_device_id, | |
| 325 PortableDeviceWatcherWin::StorageObjects* storage_objects) { | |
| 326 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 327 DCHECK(storage_objects); | |
| 328 base::win::ScopedComPtr<IPortableDevice> device; | |
| 329 if (!SetUp(pnp_device_id, &device)) | |
| 330 return false; | |
| 331 | |
| 332 string16 device_serial_num; | |
| 333 if (!GetObjectUniqueId(device.get(), WPD_DEVICE_OBJECT_ID, | |
| 334 &device_serial_num)) { | |
| 335 return false; | |
| 336 } | |
| 337 | |
| 338 PortableDeviceWatcherWin::StorageObjectIDs storage_obj_ids; | |
| 339 if (!GetRemovableStorageObjectIds(device.get(), &storage_obj_ids)) | |
| 340 return false; | |
| 341 | |
| 342 for (PortableDeviceWatcherWin::StorageObjectIDs::const_iterator id_iter = | |
| 343 storage_obj_ids.begin(); | |
| 344 id_iter != storage_obj_ids.end(); ++id_iter) { | |
| 345 string16 storage_persistent_id; | |
| 346 if (!GetObjectUniqueId(device.get(), *id_iter, &storage_persistent_id)) | |
| 347 continue; | |
| 348 | |
| 349 std::string device_storage_id; | |
| 350 if (ConstructDeviceStorageUniqueId(device_serial_num, storage_persistent_id, | |
| 351 &device_storage_id)) { | |
| 352 storage_objects->push_back(PortableDeviceWatcherWin::DeviceStorageObject( | |
| 353 *id_iter, device_storage_id)); | |
| 354 } | |
| 355 } | |
| 356 return true; | |
| 357 } | |
| 358 | |
| 359 // Accesses the device and gets the device details (name, storage info, etc). | |
| 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 | |
|
Peter Kasting
2012/10/29 21:57:19
Nit: No blank line after precondition DCHECKs (sin
kmadhusu
2012/10/30 01:29:24
Done.
| |
| 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 (Windows XP with old versions of | |
| 396 // Media Player) 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 |devices| with the attached portable device details. On failure, | |
| 405 // |result| will be assigned false. | |
| 406 void EnumerateAttachedDevicesOnBlockingThread( | |
| 407 PortableDeviceWatcherWin::Devices* devices, | |
| 408 bool* result) { | |
| 409 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 410 DCHECK(devices); | |
| 411 DCHECK(result); | |
| 412 *result = false; | |
| 413 base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr; | |
| 414 if (!GetPortableDeviceManager(&portable_device_mgr)) | |
| 415 return; | |
| 416 | |
| 417 // Get the total number of devices found on the system. | |
| 418 DWORD pnp_device_count = 0; | |
| 419 HRESULT hr = portable_device_mgr->GetDevices(NULL, &pnp_device_count); | |
| 420 if (FAILED(hr)) | |
| 421 return; | |
| 422 | |
| 423 scoped_array<char16*> pnp_device_ids(new char16*[pnp_device_count]); | |
| 424 ZeroMemory(pnp_device_ids.get(), pnp_device_count); | |
| 425 hr = portable_device_mgr->GetDevices(pnp_device_ids.get(), &pnp_device_count); | |
| 426 if (FAILED(hr)) | |
| 427 return; | |
| 428 | |
| 429 for (DWORD index = 0; index < pnp_device_count; ++index) { | |
| 430 PortableDeviceWatcherWin::DeviceDetails device_details; | |
| 431 if (!GetDeviceInfoOnBlockingThread(portable_device_mgr, | |
|
Peter Kasting
2012/10/29 21:57:19
Nit: Or just:
if (GetDeviceInfoOnBlockingThre
kmadhusu
2012/10/30 01:29:24
Done.
| |
| 432 pnp_device_ids[index], | |
| 433 &device_details)) { | |
| 434 continue; | |
| 435 } | |
| 436 devices->push_back(device_details); | |
| 437 } | |
| 438 *result = !devices->empty(); | |
| 439 } | |
| 440 | |
| 441 // Handles the device attach event message on a media task runner. This function | |
| 442 // communicates with the device to get the storage details. This task may take a | |
| 443 // longer time. Therefore, access this function on a media task runner. | |
| 444 // |pnp_device_id| specifies the attached plug and play device ID string. On | |
| 445 // success, populates |device_details| with device information. On failure, | |
| 446 // |result| will be assigned false. | |
| 447 void HandleDeviceAttachedEventOnBlockingThread( | |
| 448 const string16& pnp_device_id, | |
| 449 PortableDeviceWatcherWin::DeviceDetails* device_details, | |
| 450 bool* result) { | |
|
Peter Kasting
2012/10/29 21:57:19
You should be able to avoid this outparam and retu
kmadhusu
2012/10/30 01:29:24
Done.
| |
| 451 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 452 DCHECK(device_details); | |
| 453 DCHECK(result); | |
| 454 base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr; | |
| 455 if (!GetPortableDeviceManager(&portable_device_mgr)) { | |
| 456 *result = false; | |
| 457 return; | |
| 458 } | |
| 459 // Sometimes, portable device manager doesn't have the new device details. | |
| 460 // Refresh the manager device list to update its details. | |
| 461 portable_device_mgr->RefreshDeviceList(); | |
| 462 *result = GetDeviceInfoOnBlockingThread(portable_device_mgr, pnp_device_id, | |
| 463 device_details); | |
| 464 } | |
| 465 | |
| 466 // Constructs and returns a storage path from storage unique identifier. | |
| 467 string16 GetStoragePathFromStorageId(const std::string& storage_unique_id) { | |
| 468 // Construct a dummy device path using the storage name. This is only used | |
| 469 // for registering the device media file system. | |
| 470 DCHECK(!storage_unique_id.empty()); | |
| 471 return UTF8ToUTF16("\\\\" + storage_unique_id); | |
| 472 } | |
| 473 | |
| 474 } // namespace | |
| 475 | |
|
Peter Kasting
2012/10/29 21:57:19
Nit: Consider adding one more blank line here to m
kmadhusu
2012/10/30 01:29:24
Done.
| |
| 476 // PortableDeviceWatcherWin --------------------------------------------------- | |
| 477 | |
| 478 PortableDeviceWatcherWin::DeviceStorageObject::DeviceStorageObject( | |
| 479 const string16& temporary_id, | |
| 480 const std::string& persistent_id) | |
| 481 : object_temporary_id(temporary_id), | |
| 482 object_persistent_id(persistent_id) { | |
| 483 } | |
| 484 | |
| 485 PortableDeviceWatcherWin::PortableDeviceWatcherWin() | |
| 486 : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | |
| 487 } | |
| 488 | |
| 489 PortableDeviceWatcherWin::~PortableDeviceWatcherWin() { | |
| 490 } | |
| 491 | |
| 492 void PortableDeviceWatcherWin::Init() { | |
| 493 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 494 | |
| 495 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); | |
| 496 base::SequencedWorkerPool::SequenceToken media_sequence_token = | |
| 497 pool->GetNamedSequenceToken(kMediaTaskRunnerName); | |
|
Peter Kasting
2012/10/29 21:57:19
Nit: The declaration here is so verbose I'd just i
kmadhusu
2012/10/30 01:29:24
Done.
| |
| 498 media_task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior( | |
| 499 media_sequence_token, base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); | |
| 500 EnumerateAttachedDevices(); | |
| 501 } | |
| 502 | |
| 503 void PortableDeviceWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) { | |
| 504 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 505 | |
| 506 if (!IsPortableDeviceStructure(data)) | |
| 507 return; | |
| 508 | |
| 509 string16 device_id = GetPnpDeviceId(data); | |
| 510 if (event_type == DBT_DEVICEARRIVAL) | |
| 511 HandleDeviceAttachEvent(device_id); | |
| 512 else if (event_type == DBT_DEVICEREMOVECOMPLETE) | |
| 513 HandleDeviceDetachEvent(device_id); | |
| 514 } | |
| 515 | |
| 516 void PortableDeviceWatcherWin::EnumerateAttachedDevices() { | |
| 517 DCHECK(media_task_runner_.get()); | |
| 518 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 519 | |
| 520 Devices* devices = new Devices; | |
| 521 bool* result = new bool(false); | |
| 522 media_task_runner_->PostTaskAndReply( | |
| 523 FROM_HERE, | |
| 524 base::Bind(&EnumerateAttachedDevicesOnBlockingThread, devices, result), | |
| 525 base::Bind(&PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices, | |
| 526 weak_ptr_factory_.GetWeakPtr(), base::Owned(devices), | |
| 527 base::Owned(result))); | |
| 528 } | |
| 529 | |
| 530 void PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices( | |
| 531 const Devices* devices, const bool* result) { | |
| 532 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 533 DCHECK(devices); | |
| 534 DCHECK(result); | |
| 535 if (!(*result)) | |
| 536 return; | |
| 537 | |
| 538 for (Devices::const_iterator device_iter = devices->begin(); | |
| 539 device_iter != devices->end(); ++device_iter) { | |
| 540 OnDidHandleDeviceAttachEvent(&(*device_iter), result); | |
| 541 } | |
| 542 } | |
| 543 | |
| 544 void PortableDeviceWatcherWin::HandleDeviceAttachEvent( | |
| 545 const string16& pnp_device_id) { | |
| 546 DCHECK(media_task_runner_.get()); | |
| 547 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 548 | |
| 549 DeviceDetails* device_details = new DeviceDetails; | |
| 550 bool* result = new bool(false); | |
| 551 media_task_runner_->PostTaskAndReply( | |
| 552 FROM_HERE, | |
| 553 base::Bind(&HandleDeviceAttachedEventOnBlockingThread, pnp_device_id, | |
| 554 device_details, result), | |
| 555 base::Bind(&PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent, | |
| 556 weak_ptr_factory_.GetWeakPtr(), base::Owned(device_details), | |
| 557 base::Owned(result))); | |
| 558 } | |
| 559 | |
| 560 void PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent( | |
| 561 const DeviceDetails* device_details, const bool* result) { | |
| 562 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 563 DCHECK(device_details); | |
| 564 DCHECK(result); | |
| 565 | |
| 566 if (!(*result)) | |
| 567 return; | |
| 568 | |
| 569 const StorageObjects& storage_objects = device_details->storage_objects; | |
| 570 const string16& name = device_details->name; | |
| 571 const string16& location = device_details->location; | |
| 572 DCHECK(!ContainsKey(device_map_, location)); | |
| 573 | |
| 574 base::SystemMonitor* system_monitor = base::SystemMonitor::Get(); | |
| 575 DCHECK(system_monitor); | |
| 576 for (StorageObjects::const_iterator storage_iter = storage_objects.begin(); | |
| 577 storage_iter != storage_objects.end(); ++storage_iter) { | |
| 578 const std::string& storage_id = storage_iter->object_persistent_id; | |
| 579 DCHECK(!ContainsKey(storage_map_, storage_id)); | |
| 580 | |
| 581 // Keep track of storage id and storage name to see how often we receive | |
| 582 // empty values. | |
| 583 MediaStorageUtil::RecordDeviceInfoHistogram(false, storage_id, name); | |
| 584 if (storage_id.empty() || name.empty()) | |
| 585 return; | |
| 586 | |
| 587 // Device can have several data partitions. Therefore, add the | |
| 588 // partition identifier to the storage name. E.g.: "Nexus 7 (s10001)" | |
| 589 string16 storage_name(name + L" (" + storage_iter->object_temporary_id + | |
| 590 L')'); | |
| 591 storage_map_[storage_id] = | |
| 592 base::SystemMonitor::RemovableStorageInfo(storage_id, storage_name, | |
| 593 location); | |
| 594 system_monitor->ProcessRemovableStorageAttached( | |
| 595 storage_id, storage_name, GetStoragePathFromStorageId(storage_id)); | |
| 596 } | |
| 597 device_map_[location] = storage_objects; | |
| 598 } | |
| 599 | |
| 600 void PortableDeviceWatcherWin::HandleDeviceDetachEvent( | |
| 601 const string16& pnp_device_id) { | |
| 602 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 603 MtpDeviceMap::iterator device_iter = device_map_.find(pnp_device_id); | |
| 604 if (device_iter == device_map_.end()) | |
| 605 return; | |
| 606 | |
| 607 base::SystemMonitor* system_monitor = base::SystemMonitor::Get(); | |
| 608 DCHECK(system_monitor); | |
| 609 | |
| 610 const StorageObjects& storage_objects = device_iter->second; | |
| 611 for (StorageObjects::const_iterator storage_object_iter = | |
| 612 storage_objects.begin(); | |
| 613 storage_object_iter != storage_objects.end(); ++storage_object_iter) { | |
| 614 std::string storage_id = storage_object_iter->object_persistent_id; | |
| 615 MtpStorageMap::iterator storage_iter = storage_map_.find(storage_id); | |
| 616 DCHECK(storage_iter != storage_map_.end()); | |
| 617 system_monitor->ProcessRemovableStorageDetached( | |
| 618 storage_iter->second.device_id); | |
| 619 storage_map_.erase(storage_iter); | |
| 620 } | |
| 621 device_map_.erase(device_iter); | |
| 622 } | |
| 623 | |
| 624 } // namespace chrome | |
| OLD | NEW |