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