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 // MTPDeviceOperationsUtil implementation. | |
| 6 | |
| 7 #include "chrome/browser/media_gallery/win/mtp_device_operations_util.h" | |
| 8 | |
| 9 #include <portabledevice.h> | |
| 10 | |
| 11 #include "base/logging.h" | |
| 12 #include "base/string_util.h" | |
| 13 #include "base/threading/sequenced_worker_pool.h" | |
| 14 #include "base/time.h" | |
| 15 #include "base/win/scoped_co_mem.h" | |
| 16 #include "chrome/browser/system_monitor/removable_device_constants.h" | |
| 17 #include "content/public/browser/browser_thread.h" | |
| 18 | |
| 19 namespace chrome { | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 // Name of the client application that communicates with the MTP device. | |
| 24 const char16 kClientName[] = L"Chromium"; | |
|
Peter Kasting
2012/11/01 21:38:37
This is a bit unusual. I assume it doesn't matter
kmadhusu
2012/11/02 03:27:16
Fixed. I didn't know about this constant. It doesn
| |
| 25 | |
| 26 // On success, returns true and updates |client_info| with a reference to an | |
| 27 // IPortableDeviceValues interface that holds information about the | |
| 28 // application that communicates with the device. | |
| 29 bool GetClientInformation( | |
| 30 base::win::ScopedComPtr<IPortableDeviceValues>* client_info) { | |
| 31 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
|
Peter Kasting
2012/11/01 21:38:37
Seems like at least some of these utilities won't
kmadhusu
2012/11/02 03:27:16
Most of these functions just reads the properties
| |
| 32 DCHECK(client_info); | |
| 33 HRESULT hr = client_info->CreateInstance(__uuidof(PortableDeviceValues), | |
| 34 NULL, CLSCTX_INPROC_SERVER); | |
| 35 if (FAILED(hr)) { | |
| 36 DPLOG(ERROR) << "Failed to create an instance of IPortableDeviceValues"; | |
| 37 return false; | |
| 38 } | |
| 39 | |
| 40 // Attempt to set client details. | |
| 41 (*client_info)->SetStringValue(WPD_CLIENT_NAME, kClientName); | |
| 42 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, 0); | |
| 43 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, 0); | |
| 44 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_REVISION, 0); | |
| 45 (*client_info)->SetUnsignedIntegerValue( | |
| 46 WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE, SECURITY_IMPERSONATION); | |
| 47 (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS, | |
| 48 GENERIC_READ); | |
| 49 return true; | |
| 50 } | |
| 51 | |
| 52 // Gets the content interface of the portable |device|. On success, returns | |
| 53 // true and fills in |content| with the given portable |device| content. | |
| 54 bool GetDeviceContent(IPortableDevice* device, | |
| 55 IPortableDeviceContent** content) { | |
| 56 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 57 DCHECK(device); | |
| 58 DCHECK(content); | |
| 59 return SUCCEEDED(device->Content(content)); | |
| 60 } | |
| 61 | |
| 62 // Gets the portable |device| enumerator interface to enumerate the objects. | |
| 63 // |parent_id| specifies the parent object identifier. On success, returns | |
| 64 // true and fills in |enum_object_ids|. On failure, returns false and | |
| 65 // |enum_object_ids| is not set. | |
| 66 bool GetDeviceObjectEnumerator(IPortableDevice* device, | |
| 67 const string16& parent_id, | |
| 68 IEnumPortableDeviceObjectIDs** enum_object_ids) { | |
| 69 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 70 DCHECK(device); | |
| 71 DCHECK(!parent_id.empty()); | |
| 72 DCHECK(enum_object_ids); | |
| 73 base::win::ScopedComPtr<IPortableDeviceContent> content; | |
| 74 if (!GetDeviceContent(device, content.Receive())) | |
| 75 return false; | |
| 76 | |
| 77 HRESULT hr = content->EnumObjects(0, parent_id.c_str(), NULL, | |
| 78 enum_object_ids); | |
| 79 return SUCCEEDED(hr); | |
| 80 } | |
| 81 | |
| 82 // Reads data from |stream| into a |data| string. | |
| 83 void ReadStream(IStream* stream, size_t size, std::string* data) { | |
| 84 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 85 DCHECK(stream); | |
| 86 DCHECK_GT(size, 0u); | |
| 87 DCHECK(data); | |
| 88 DWORD read = 0; | |
| 89 HRESULT hr = S_OK; | |
| 90 do { | |
| 91 std::string buffer; | |
| 92 hr = stream->Read(WriteInto(&buffer, size + 1), size, &read); | |
| 93 DCHECK(hr == S_OK || hr == S_FALSE || hr == E_PENDING); | |
| 94 if (read) { | |
| 95 buffer.erase(read); | |
| 96 DCHECK_EQ(read, buffer.length()); | |
| 97 *data += buffer; | |
| 98 } | |
| 99 } while ((read > 0) && SUCCEEDED(hr)); | |
| 100 } | |
| 101 | |
| 102 // Returns true, if the object is a directory/folder/album otherwise returns | |
|
Peter Kasting
2012/11/01 21:38:37
Nit: First sentence can be just "Returns whether t
kmadhusu
2012/11/02 03:27:16
Done.
| |
| 103 // false. |properties_values| contains the object property key values. | |
| 104 bool IsDirectory(IPortableDeviceValues* properties_values) { | |
| 105 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 106 DCHECK(properties_values); | |
| 107 GUID content_type; | |
| 108 HRESULT hr = properties_values->GetGuidValue(WPD_OBJECT_CONTENT_TYPE, | |
| 109 &content_type); | |
| 110 DCHECK(SUCCEEDED(hr)); | |
| 111 return ((content_type == WPD_CONTENT_TYPE_AUDIO_ALBUM) || | |
| 112 (content_type == WPD_CONTENT_TYPE_FOLDER) || | |
| 113 (content_type == WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT) || | |
| 114 (content_type == WPD_CONTENT_TYPE_IMAGE_ALBUM) || | |
| 115 (content_type == WPD_CONTENT_TYPE_MIXED_CONTENT_ALBUM) || | |
| 116 (content_type == WPD_CONTENT_TYPE_VIDEO_ALBUM)); | |
| 117 } | |
| 118 | |
| 119 // Returns the object extension type based on the object format specified | |
| 120 // by the |properties_values|. If the object is an unsupport format, this | |
| 121 // function returns an empty string. | |
| 122 string16 GetObjectExtension(IPortableDeviceValues* properties_values) { | |
| 123 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 124 DCHECK(properties_values); | |
| 125 GUID format; | |
| 126 HRESULT hr = properties_values->GetGuidValue(WPD_OBJECT_FORMAT, &format); | |
| 127 DCHECK(SUCCEEDED(hr)); | |
| 128 if (IsEqualGUID(WPD_OBJECT_FORMAT_BMP, format)) | |
| 129 return L".bmp"; | |
|
Peter Kasting
2012/11/01 21:38:37
Nit: Extra space (several places)
kmadhusu
2012/11/02 03:27:16
oops. Fixed.
| |
| 130 if (IsEqualGUID(WPD_OBJECT_FORMAT_EXIF, format)) | |
| 131 return L".jpg"; | |
| 132 if (IsEqualGUID(WPD_OBJECT_FORMAT_GIF, format)) | |
| 133 return L".gif"; | |
| 134 if (IsEqualGUID(WPD_OBJECT_FORMAT_JFIF, format)) | |
| 135 return L".jfif"; | |
| 136 if (IsEqualGUID(WPD_OBJECT_FORMAT_JP2, format)) | |
| 137 return L".jxr"; | |
| 138 if (IsEqualGUID(WPD_OBJECT_FORMAT_JPEGXR, format)) | |
| 139 return L".jpx"; | |
| 140 if (IsEqualGUID(WPD_OBJECT_FORMAT_JPX, format)) | |
| 141 return L".jp2"; | |
| 142 if (IsEqualGUID(WPD_OBJECT_FORMAT_PICT, format)) | |
| 143 return L".pic"; | |
| 144 if (IsEqualGUID(WPD_OBJECT_FORMAT_PNG, format)) | |
| 145 return L".png"; | |
| 146 return string16(); | |
| 147 } | |
| 148 | |
| 149 // Gets the friendly name of the object from the property key values specified | |
| 150 // by the |properties_values|. On success, returns true and fills in |name|. | |
| 151 // On failure, returns false and |name| is not set. | |
| 152 bool GetObjectName(IPortableDeviceValues* properties_values, | |
| 153 bool is_directory, | |
| 154 string16* name) { | |
| 155 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 156 DCHECK(properties_values); | |
| 157 DCHECK(name); | |
| 158 base::win::ScopedCoMem<char16> buffer; | |
| 159 HRESULT hr = properties_values->GetStringValue(WPD_OBJECT_NAME, &buffer); | |
| 160 DCHECK(SUCCEEDED(hr)); | |
| 161 *name = static_cast<const char16*>(buffer); | |
| 162 if (name->empty()) | |
| 163 return false; | |
|
Peter Kasting
2012/11/01 21:38:37
Here you wrote |name| and then returned false, whi
kmadhusu
2012/11/02 03:27:16
I would like to return bool. Fixed the comment to
Peter Kasting
2012/11/02 04:08:11
What does returning a bool buy you over just retur
kmadhusu
2012/11/29 23:58:14
Your argument convinced me. Returning the name ins
| |
| 164 if (is_directory) | |
| 165 return true; | |
| 166 | |
| 167 // Some MTP device objects does not have extension as part of their name. | |
| 168 // Check to see if an extension exists in the given object name. | |
| 169 if (name->find_first_of(L".") == string16::npos) | |
| 170 *name += GetObjectExtension(properties_values); | |
| 171 return true; | |
| 172 } | |
| 173 | |
| 174 // Gets the last modified time of the object from the property key values | |
| 175 // specified by the |properties_values|. On success, returns true and fills in | |
| 176 // |last_modified_time|. On failure, returns false and |last_modified_time| is | |
| 177 // not set. | |
| 178 bool GetLastModifiedTime(IPortableDeviceValues* properties_values, | |
| 179 base::Time* last_modified_time) { | |
| 180 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 181 DCHECK(properties_values); | |
| 182 DCHECK(last_modified_time); | |
| 183 PROPVARIANT last_modified_date = {0}; | |
| 184 PropVariantInit(&last_modified_date); | |
| 185 HRESULT hr = properties_values->GetValue(WPD_OBJECT_DATE_MODIFIED, | |
| 186 &last_modified_date); | |
| 187 if (FAILED(hr)) | |
| 188 return false; | |
| 189 | |
| 190 if (SUCCEEDED(hr) && last_modified_date.vt == VT_DATE) { | |
|
Peter Kasting
2012/11/01 21:38:37
Nit: SUCCEEDED(hr) is guaranteed true here
kmadhusu
2012/11/02 03:27:16
Removed SUCCEEDED(hr).
| |
| 191 SYSTEMTIME system_time; | |
| 192 FILETIME file_time; | |
| 193 if (VariantTimeToSystemTime(last_modified_date.date, &system_time) && | |
| 194 SystemTimeToFileTime(&system_time, &file_time)) | |
| 195 *last_modified_time = base::Time::FromFileTime(file_time); | |
| 196 } | |
| 197 PropVariantClear(&last_modified_date); | |
| 198 return true; | |
|
Peter Kasting
2012/11/01 21:38:37
Doesn't seem like this should return true in cases
kmadhusu
2012/11/02 03:27:16
Some MTP devices does not set the modification tim
Peter Kasting
2012/11/02 04:08:11
The point is that last_modified_time may be entire
kmadhusu
2012/11/29 23:58:14
I have a test device (Canon powershot A70 camera)
| |
| 199 } | |
| 200 | |
| 201 // Gets the size of the file object in bytes from the property key values | |
| 202 // specified by the |properties_values|. On success, returns true and fills | |
| 203 // in |size|. On failure, returns false and |size| is not set. | |
| 204 bool GetObjectSize(IPortableDeviceValues* properties_values, int64* size) { | |
| 205 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 206 DCHECK(properties_values); | |
| 207 DCHECK(size); | |
| 208 HRESULT hr = properties_values->GetUnsignedLargeIntegerValue( | |
| 209 WPD_OBJECT_SIZE, reinterpret_cast<ULONGLONG*>(size)); | |
| 210 return SUCCEEDED(hr); | |
| 211 } | |
| 212 | |
| 213 // Gets the details of the object specified by the |object_id| given the media | |
| 214 // transfer protocol |device|. On success, returns true and fills in |name|, | |
| 215 // |is_directory|, |size| and |last_modified_time|. On failure, returns false | |
| 216 // and all the output params are not set. | |
| 217 bool GetObjectDetails(IPortableDevice* device, | |
| 218 const string16 object_id, | |
| 219 string16* name, | |
| 220 bool* is_directory, | |
| 221 int64* size, | |
| 222 base::Time* last_modified_time) { | |
| 223 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 224 DCHECK(device); | |
| 225 DCHECK(!object_id.empty()); | |
| 226 DCHECK(name); | |
| 227 DCHECK(is_directory); | |
| 228 DCHECK(size); | |
| 229 DCHECK(last_modified_time); | |
| 230 base::win::ScopedComPtr<IPortableDeviceContent> content; | |
| 231 if (!GetDeviceContent(device, content.Receive())) | |
| 232 return false; | |
| 233 | |
| 234 base::win::ScopedComPtr<IPortableDeviceProperties> properties; | |
| 235 HRESULT hr = content->Properties(properties.Receive()); | |
| 236 if (FAILED(hr)) | |
| 237 return false; | |
| 238 | |
| 239 base::win::ScopedComPtr<IPortableDeviceKeyCollection> properties_to_read; | |
| 240 hr = properties_to_read.CreateInstance(__uuidof(PortableDeviceKeyCollection), | |
| 241 NULL, | |
| 242 CLSCTX_INPROC_SERVER); | |
| 243 if (FAILED(hr)) | |
| 244 return false; | |
| 245 | |
| 246 if (FAILED(properties_to_read->Add(WPD_OBJECT_CONTENT_TYPE)) || | |
| 247 FAILED(properties_to_read->Add(WPD_OBJECT_FORMAT)) || | |
| 248 FAILED(properties_to_read->Add(WPD_OBJECT_NAME)) || | |
| 249 FAILED(properties_to_read->Add(WPD_OBJECT_DATE_MODIFIED)) || | |
| 250 FAILED(properties_to_read->Add(WPD_OBJECT_SIZE))) | |
| 251 return false; | |
| 252 | |
| 253 base::win::ScopedComPtr<IPortableDeviceValues> properties_values; | |
| 254 hr = properties->GetValues(object_id.c_str(), | |
| 255 properties_to_read.get(), | |
| 256 properties_values.Receive()); | |
| 257 if (FAILED(hr)) | |
| 258 return false; | |
| 259 | |
| 260 *is_directory = IsDirectory(properties_values.get()); | |
| 261 if (!GetObjectName(properties_values.get(), *is_directory, name)) | |
| 262 return false; | |
|
Peter Kasting
2012/11/01 21:38:37
More cases where you return false after writing to
kmadhusu
2012/11/02 03:27:16
Fixed comment.
| |
| 263 | |
| 264 if (*is_directory) { | |
| 265 // Directory entry does not have size and last modified date property key | |
| 266 // values. | |
| 267 *size = 0; | |
| 268 *last_modified_time = base::Time(); | |
| 269 return true; | |
| 270 } | |
| 271 return (GetObjectSize(properties_values.get(), size) && | |
| 272 GetLastModifiedTime(properties_values.get(), last_modified_time)); | |
| 273 } | |
| 274 | |
| 275 // Creates a MTP device object entry for the given |device| and |object_id|. | |
|
Peter Kasting
2012/11/01 21:38:37
Nit: a -> an
kmadhusu
2012/11/02 03:27:16
Done.
| |
| 276 // On success, returns true and fills in |entry|. On failure, returns false | |
| 277 // and |entry| is not set. | |
| 278 bool GetMTPDeviceObjectEntry(IPortableDevice* device, | |
| 279 const string16& object_id, | |
| 280 MTPDeviceObjectEntry* entry) { | |
| 281 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 282 DCHECK(device); | |
| 283 DCHECK(!object_id.empty()); | |
| 284 DCHECK(entry); | |
| 285 string16 name; | |
| 286 bool is_directory; | |
| 287 int64 size; | |
| 288 base::Time last_modified_time; | |
| 289 if (!GetObjectDetails(device, object_id, &name, &is_directory, &size, | |
| 290 &last_modified_time)) | |
| 291 return false; | |
| 292 *entry = MTPDeviceObjectEntry(object_id, name, is_directory, size, | |
| 293 last_modified_time); | |
| 294 return true; | |
| 295 } | |
| 296 | |
| 297 } // namespace | |
| 298 | |
| 299 | |
| 300 // MTPDeviceOperationsUtil ---------------------------------------------------- | |
| 301 | |
| 302 // static | |
| 303 bool MTPDeviceOperationsUtil::OpenDevice( | |
| 304 const string16& pnp_device_id, | |
| 305 base::win::ScopedComPtr<IPortableDevice>* device) { | |
| 306 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 307 DCHECK(!pnp_device_id.empty()); | |
| 308 DCHECK(device); | |
| 309 base::win::ScopedComPtr<IPortableDeviceValues> client_info; | |
| 310 if (!GetClientInformation(&client_info)) | |
| 311 return false; | |
| 312 HRESULT hr = device->CreateInstance(__uuidof(PortableDevice), NULL, | |
| 313 CLSCTX_INPROC_SERVER); | |
| 314 if (FAILED(hr)) | |
| 315 return false; | |
| 316 | |
| 317 hr = (*device)->Open(pnp_device_id.c_str(), client_info.get()); | |
| 318 if (SUCCEEDED(hr)) | |
| 319 return true; | |
| 320 if (hr == E_ACCESSDENIED) | |
| 321 DPLOG(ERROR) << "Access denied to open the device"; | |
| 322 return false; | |
| 323 } | |
| 324 | |
| 325 // static | |
| 326 base::PlatformFileError MTPDeviceOperationsUtil::GetFileEntryInfo( | |
| 327 IPortableDevice* device, | |
| 328 const string16& object_id, | |
| 329 base::PlatformFileInfo* file_entry_info) { | |
| 330 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 331 DCHECK(device); | |
| 332 DCHECK(!object_id.empty()); | |
| 333 DCHECK(file_entry_info); | |
| 334 MTPDeviceObjectEntry entry; | |
| 335 if (!GetMTPDeviceObjectEntry(device, object_id, &entry)) | |
| 336 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 337 | |
| 338 file_entry_info->size = entry.size(); | |
| 339 file_entry_info->is_directory = entry.is_directory(); | |
| 340 file_entry_info->is_symbolic_link = false; | |
| 341 file_entry_info->last_modified = entry.last_modified_time(); | |
| 342 file_entry_info->last_accessed = entry.last_modified_time(); | |
| 343 file_entry_info->creation_time = base::Time(); | |
| 344 return base::PLATFORM_FILE_OK; | |
| 345 } | |
| 346 | |
| 347 // static | |
| 348 bool MTPDeviceOperationsUtil::GetDirectoryEntries( | |
| 349 IPortableDevice* device, | |
| 350 const string16& directory_object_id, | |
| 351 MTPDeviceObjectEntries* object_entries) { | |
| 352 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 353 DCHECK(device); | |
| 354 DCHECK(!directory_object_id.empty()); | |
| 355 DCHECK(object_entries); | |
| 356 base::win::ScopedComPtr<IEnumPortableDeviceObjectIDs> enum_object_ids; | |
| 357 if (!GetDeviceObjectEnumerator(device, directory_object_id, | |
| 358 enum_object_ids.Receive())) | |
| 359 return false; | |
| 360 | |
| 361 // Loop calling Next() while S_OK is being returned. | |
| 362 DWORD num_objects_to_request = 10; | |
| 363 HRESULT hr = S_OK; | |
| 364 while (hr == S_OK) { | |
|
Peter Kasting
2012/11/01 21:38:37
Nit: Or
for (HRESULT hr = S_OK; hr == S_OK; ) {
kmadhusu
2012/11/02 03:27:16
Done.
| |
| 365 DWORD num_objects_fetched = 0; | |
|
Peter Kasting
2012/11/01 21:38:37
Nit: extra space
kmadhusu
2012/11/02 03:27:16
Done.
| |
| 366 scoped_array<char16*> object_ids(new char16*[num_objects_to_request]); | |
| 367 hr = enum_object_ids->Next(num_objects_to_request, object_ids.get(), | |
| 368 &num_objects_fetched); | |
| 369 for (DWORD index = 0; index < num_objects_fetched; index++) { | |
|
Peter Kasting
2012/11/01 21:38:37
Nit: Prefer preincrement whenever possible
kmadhusu
2012/11/02 03:27:16
Done.
| |
| 370 MTPDeviceObjectEntry entry; | |
| 371 if (GetMTPDeviceObjectEntry(device, object_ids[index], &entry)) | |
| 372 object_entries->push_back(entry); | |
| 373 } | |
| 374 } | |
| 375 return true; | |
| 376 } | |
| 377 | |
| 378 // static | |
| 379 bool MTPDeviceOperationsUtil::GetFileObjectData(IPortableDevice* device, | |
| 380 const string16& file_object_id, | |
| 381 std::string* file_data) { | |
| 382 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 383 DCHECK(device); | |
| 384 DCHECK(!file_object_id.empty()); | |
| 385 DCHECK(file_data); | |
| 386 base::win::ScopedComPtr<IPortableDeviceContent> content; | |
| 387 if (!GetDeviceContent(device, content.Receive())) | |
| 388 return false; | |
| 389 | |
| 390 base::win::ScopedComPtr<IPortableDeviceResources> resources; | |
| 391 HRESULT hr = content->Transfer(resources.Receive()); | |
| 392 if (FAILED(hr)) | |
| 393 return false; | |
| 394 | |
| 395 base::win::ScopedComPtr<IStream> file_stream; | |
| 396 DWORD optimal_transfer_size = 0; | |
| 397 hr = resources->GetStream(file_object_id.c_str(), WPD_RESOURCE_DEFAULT, | |
| 398 STGM_READ, &optimal_transfer_size, | |
| 399 file_stream.Receive()); | |
| 400 if (FAILED(hr)) | |
| 401 return false; | |
| 402 ReadStream(file_stream.get(), optimal_transfer_size, file_data); | |
| 403 return true; | |
| 404 } | |
| 405 | |
| 406 // static | |
| 407 string16 MTPDeviceOperationsUtil::GetObjectIdFromName( | |
| 408 IPortableDevice* device, | |
| 409 const string16& parent_id, | |
| 410 const string16& object_name) { | |
|
Peter Kasting
2012/11/01 21:38:37
This function seems very similar to GetDirectoryEn
kmadhusu
2012/11/02 03:27:16
Done. Created a helper function "GetMTPDeviceObjec
| |
| 411 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); | |
| 412 DCHECK(device); | |
| 413 DCHECK(!parent_id.empty()); | |
| 414 DCHECK(!object_name.empty()); | |
| 415 base::win::ScopedComPtr<IEnumPortableDeviceObjectIDs> enum_object_ids; | |
| 416 if (!GetDeviceObjectEnumerator(device, parent_id, enum_object_ids.Receive())) | |
| 417 return string16(); | |
| 418 | |
| 419 DWORD num_objects_to_request = 10; | |
| 420 HRESULT hr = S_OK; | |
| 421 while (hr == S_OK) { | |
| 422 DWORD num_objects_fetched = 0; | |
| 423 scoped_array<char16*> object_ids(new char16*[num_objects_to_request]); | |
| 424 hr = enum_object_ids->Next(num_objects_to_request, object_ids.get(), | |
| 425 &num_objects_fetched); | |
| 426 for (DWORD index = 0; index < num_objects_fetched; index++) { | |
| 427 MTPDeviceObjectEntry entry; | |
| 428 if (!GetMTPDeviceObjectEntry(device, object_ids[index], &entry)) | |
| 429 return string16(); | |
| 430 if (entry.file_name() == object_name) | |
| 431 return entry.object_id(); | |
| 432 } | |
| 433 } | |
| 434 return string16(); | |
| 435 } | |
| 436 | |
| 437 MTPDeviceOperationsUtil::MTPDeviceOperationsUtil() { | |
| 438 } | |
| 439 | |
| 440 } // namespace chrome | |
| OLD | NEW |