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