Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(94)

Side by Side Diff: chrome/browser/media_gallery/win/mtp_device_operations_util.cc

Issue 11297002: [Media Gallery] Added code to support mtp device media file system on Windows. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed review comments Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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";
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());
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
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 kbmpFormat;
130 if (IsEqualGUID(WPD_OBJECT_FORMAT_EXIF, format))
131 return kexifFormat;
132 if (IsEqualGUID(WPD_OBJECT_FORMAT_GIF, format))
133 return kgifFormat;
134 if (IsEqualGUID(WPD_OBJECT_FORMAT_JFIF, format))
135 return kjfifFormat;
136 if (IsEqualGUID(WPD_OBJECT_FORMAT_JP2, format))
137 return kjp2Format;
138 if (IsEqualGUID(WPD_OBJECT_FORMAT_JPEGXR, format))
139 return kjpegxrFormat;
140 if (IsEqualGUID(WPD_OBJECT_FORMAT_JPX, format))
141 return kjpxFormat;
142 if (IsEqualGUID(WPD_OBJECT_FORMAT_PICT, format))
143 return kpictFormat;
144 if (IsEqualGUID(WPD_OBJECT_FORMAT_PNG, format))
145 return kpngFormat;
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;
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) {
191 SYSTEMTIME system_time;
192 FILETIME file_time;
193 VariantTimeToSystemTime(last_modified_date.date, &system_time);
Lei Zhang 2012/11/01 02:34:57 This may fail.
kmadhusu 2012/11/01 17:59:00 Fixed.
194 SystemTimeToFileTime(&system_time, &file_time);
195 *last_modified_time = base::Time::FromFileTime(file_time);
196 }
197 PropVariantClear(&last_modified_date);
198 return true;
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;
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 } // namespace
276
277
278 // MTPDeviceOperationsUtil ----------------------------------------------------
279
280 // static
281 bool MTPDeviceOperationsUtil::OpenDevice(
282 const string16& pnp_device_id,
283 base::win::ScopedComPtr<IPortableDevice>* device) {
284 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
285 DCHECK(!pnp_device_id.empty());
286 DCHECK(device);
287 base::win::ScopedComPtr<IPortableDeviceValues> client_info;
288 if (!GetClientInformation(&client_info))
289 return false;
290 HRESULT hr = device->CreateInstance(__uuidof(PortableDevice), NULL,
291 CLSCTX_INPROC_SERVER);
292 if (FAILED(hr))
293 return false;
294
295 hr = (*device)->Open(pnp_device_id.c_str(), client_info.get());
296 if (SUCCEEDED(hr))
297 return true;
298 if (hr == E_ACCESSDENIED)
299 DPLOG(ERROR) << "Access denied to open the device";
300 return false;
301 }
302
303 // static
304 base::PlatformFileError MTPDeviceOperationsUtil::GetFileEntryInfo(
305 IPortableDevice* device,
306 const string16& object_id,
307 base::PlatformFileInfo* file_entry_info) {
308 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
309 DCHECK(device);
310 DCHECK(!object_id.empty());
311 DCHECK(file_entry_info);
312 MTPDeviceObjectEntry entry;
313 if (!GetMTPDeviceObjectEntry(device, object_id, &entry))
314 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
315
316 file_entry_info->size = entry.size();
317 file_entry_info->is_directory = entry.is_directory();
318 file_entry_info->is_symbolic_link = false;
319 file_entry_info->last_modified = entry.last_modified_time();
320 file_entry_info->last_accessed = entry.last_modified_time();
321 file_entry_info->creation_time = base::Time();
322 return base::PLATFORM_FILE_OK;
323 }
324
325 // static
326 bool MTPDeviceOperationsUtil::GetDirectoryEntries(
327 IPortableDevice* device,
328 const string16& directory_object_id,
329 MTPDeviceObjectEntries* object_entries) {
330 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
331 DCHECK(device);
332 DCHECK(!directory_object_id.empty());
333 DCHECK(object_entries);
334 base::win::ScopedComPtr<IEnumPortableDeviceObjectIDs> enum_object_ids;
335 if (!GetDeviceObjectEnumerator(device, directory_object_id,
336 enum_object_ids.Receive()))
337 return false;
338
339 // Loop calling Next() while S_OK is being returned.
340 DWORD num_objects_to_request = 10;
341 HRESULT hr = S_OK;
342 while (hr == S_OK) {
343 DWORD num_objects_fetched = 0;
344 scoped_array<char16*> object_ids(new char16*[num_objects_to_request]);
345 hr = enum_object_ids->Next(num_objects_to_request, object_ids.get(),
346 &num_objects_fetched);
347 for (DWORD index = 0; index < num_objects_fetched; index++) {
348 MTPDeviceObjectEntry entry;
349 if (GetMTPDeviceObjectEntry(device, object_ids[index], &entry))
350 object_entries->push_back(entry);
351 }
352 }
353 return true;
354 }
355
356 // static
357 bool MTPDeviceOperationsUtil::GetFileObjectData(IPortableDevice* device,
358 const string16& file_object_id,
359 std::string* file_data) {
360 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
361 DCHECK(device);
362 DCHECK(!file_object_id.empty());
363 DCHECK(file_data);
364 base::win::ScopedComPtr<IPortableDeviceContent> content;
365 if (!GetDeviceContent(device, content.Receive()))
366 return false;
367
368 base::win::ScopedComPtr<IPortableDeviceResources> resources;
369 HRESULT hr = content->Transfer(resources.Receive());
370 if (FAILED(hr))
371 return false;
372
373 base::win::ScopedComPtr<IStream> file_stream;
374 DWORD optimal_transfer_size = 0;
375 hr = resources->GetStream(file_object_id.c_str(), WPD_RESOURCE_DEFAULT,
376 STGM_READ, &optimal_transfer_size,
377 file_stream.Receive());
378 if (FAILED(hr))
379 return false;
380 ReadStream(file_stream.get(), optimal_transfer_size, file_data);
381 return true;
382 }
383
384 // static
385 string16 MTPDeviceOperationsUtil::GetObjectIdFromName(
386 IPortableDevice* device,
387 const string16& parent_id,
388 const string16& object_name) {
389 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
390 DCHECK(device);
391 DCHECK(!parent_id.empty());
392 DCHECK(!object_name.empty());
393 base::win::ScopedComPtr<IEnumPortableDeviceObjectIDs> enum_object_ids;
394 if (!GetDeviceObjectEnumerator(device, parent_id, enum_object_ids.Receive()))
395 return string16();
396
397 DWORD num_objects_to_request = 10;
398 HRESULT hr = S_OK;
399 while (hr == S_OK) {
400 DWORD num_objects_fetched = 0;
401 scoped_array<char16*> object_ids(new char16*[num_objects_to_request]);
402 hr = enum_object_ids->Next(num_objects_to_request, object_ids.get(),
403 &num_objects_fetched);
404 for (DWORD index = 0; index < num_objects_fetched; index++) {
405 MTPDeviceObjectEntry entry;
406 if (!GetMTPDeviceObjectEntry(device, object_ids[index], &entry))
407 return string16();
408 if (entry.file_name() == object_name)
409 return entry.object_id();
410 }
411 }
412 return string16();
413 }
414
415 MTPDeviceOperationsUtil::MTPDeviceOperationsUtil() {
416 }
417
418 // static
419 bool MTPDeviceOperationsUtil::GetMTPDeviceObjectEntry(
420 IPortableDevice* device,
421 const string16& object_id,
422 MTPDeviceObjectEntry* entry) {
423 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
424 DCHECK(device);
425 DCHECK(!object_id.empty());
426 DCHECK(entry);
427 string16 name;
428 bool is_directory;
429 int64 size;
430 base::Time last_modified_time;
431 if (!GetObjectDetails(device, object_id, &name, &is_directory, &size,
432 &last_modified_time))
433 return false;
434 *entry = MTPDeviceObjectEntry(object_id, name, is_directory, size,
435 last_modified_time);
436 return true;
437 }
438
439 } // namespace chrome
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698