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