Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / media_galleries / win / mtp_device_delegate_impl_win.cc
blob8dbca7f6f0e423838c5f8b467bbd7a82b1ce0480
1 // Copyright (c) 2013 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 // MTPDeviceDelegateImplWin implementation.
7 #include "chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.h"
9 #include <portabledevice.h>
11 #include <vector>
13 #include "base/bind.h"
14 #include "base/files/file_path.h"
15 #include "base/logging.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/sequenced_task_runner.h"
18 #include "base/stl_util.h"
19 #include "base/strings/string_split.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/task_runner_util.h"
23 #include "base/threading/thread_restrictions.h"
24 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
25 #include "chrome/browser/media_galleries/win/mtp_device_object_entry.h"
26 #include "chrome/browser/media_galleries/win/mtp_device_object_enumerator.h"
27 #include "chrome/browser/media_galleries/win/mtp_device_operations_util.h"
28 #include "chrome/browser/media_galleries/win/portable_device_map_service.h"
29 #include "chrome/browser/media_galleries/win/snapshot_file_details.h"
30 #include "components/storage_monitor/storage_monitor.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "storage/common/fileapi/file_system_util.h"
34 namespace {
36 // Gets the details of the MTP partition storage specified by the
37 // |storage_path| on the UI thread. Returns true if the storage details are
38 // valid and returns false otherwise.
39 bool GetStorageInfoOnUIThread(const base::string16& storage_path,
40 base::string16* pnp_device_id,
41 base::string16* storage_object_id) {
42 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
43 DCHECK(!storage_path.empty());
44 DCHECK(pnp_device_id);
45 DCHECK(storage_object_id);
46 base::string16 storage_device_id;
47 base::RemoveChars(storage_path, L"\\\\", &storage_device_id);
48 DCHECK(!storage_device_id.empty());
49 // TODO(gbillock): Take the StorageMonitor as an argument.
50 storage_monitor::StorageMonitor* monitor =
51 storage_monitor::StorageMonitor::GetInstance();
52 DCHECK(monitor);
53 return monitor->GetMTPStorageInfoFromDeviceId(
54 base::UTF16ToUTF8(storage_device_id), pnp_device_id, storage_object_id);
57 // Returns the object id of the file object specified by the |file_path|,
58 // e.g. if the |file_path| is "\\MTP:StorageSerial:SID-{1001,,192}:125\DCIM"
59 // and |device_info.registered_device_path_| is
60 // "\\MTP:StorageSerial:SID-{1001,,192}:125", this function returns the
61 // identifier of the "DCIM" folder object.
63 // Returns an empty string if the device is detached while the request is in
64 // progress or when the |file_path| is invalid.
65 base::string16 GetFileObjectIdFromPathOnBlockingPoolThread(
66 const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
67 const base::FilePath& file_path) {
68 base::ThreadRestrictions::AssertIOAllowed();
69 DCHECK(!file_path.empty());
70 IPortableDevice* device =
71 PortableDeviceMapService::GetInstance()->GetPortableDevice(
72 device_info.registered_device_path);
73 if (!device)
74 return base::string16();
76 if (device_info.registered_device_path == file_path.value())
77 return device_info.storage_object_id;
79 base::FilePath relative_path;
80 if (!base::FilePath(device_info.registered_device_path).AppendRelativePath(
81 file_path, &relative_path))
82 return base::string16();
84 std::vector<base::string16> path_components;
85 relative_path.GetComponents(&path_components);
86 DCHECK(!path_components.empty());
87 base::string16 parent_id(device_info.storage_object_id);
88 base::string16 file_object_id;
89 for (size_t i = 0; i < path_components.size(); ++i) {
90 file_object_id =
91 media_transfer_protocol::GetObjectIdFromName(device, parent_id,
92 path_components[i]);
93 if (file_object_id.empty())
94 break;
95 parent_id = file_object_id;
97 return file_object_id;
100 // Returns a pointer to a new instance of AbstractFileEnumerator for the given
101 // |root| directory. Called on a blocking pool thread.
102 scoped_ptr<MTPDeviceObjectEnumerator>
103 CreateFileEnumeratorOnBlockingPoolThread(
104 const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
105 const base::FilePath& root) {
106 base::ThreadRestrictions::AssertIOAllowed();
107 DCHECK(!device_info.registered_device_path.empty());
108 DCHECK(!root.empty());
109 IPortableDevice* device =
110 PortableDeviceMapService::GetInstance()->GetPortableDevice(
111 device_info.registered_device_path);
112 if (!device)
113 return scoped_ptr<MTPDeviceObjectEnumerator>();
115 base::string16 object_id =
116 GetFileObjectIdFromPathOnBlockingPoolThread(device_info, root);
117 if (object_id.empty())
118 return scoped_ptr<MTPDeviceObjectEnumerator>();
120 MTPDeviceObjectEntries entries;
121 if (!media_transfer_protocol::GetDirectoryEntries(device, object_id,
122 &entries) ||
123 entries.empty())
124 return scoped_ptr<MTPDeviceObjectEnumerator>();
126 return scoped_ptr<MTPDeviceObjectEnumerator>(
127 new MTPDeviceObjectEnumerator(entries));
130 // Opens the device for communication on a blocking pool thread.
131 // |pnp_device_id| specifies the PnP device id.
132 // |registered_device_path| specifies the registered file system root path for
133 // the given device.
134 bool OpenDeviceOnBlockingPoolThread(
135 const base::string16& pnp_device_id,
136 const base::string16& registered_device_path) {
137 base::ThreadRestrictions::AssertIOAllowed();
138 DCHECK(!pnp_device_id.empty());
139 DCHECK(!registered_device_path.empty());
140 base::win::ScopedComPtr<IPortableDevice> device =
141 media_transfer_protocol::OpenDevice(pnp_device_id);
142 bool init_succeeded = device.get() != NULL;
143 if (init_succeeded) {
144 PortableDeviceMapService::GetInstance()->AddPortableDevice(
145 registered_device_path, device.get());
147 return init_succeeded;
150 // Gets the |file_path| details from the MTP device specified by the
151 // |device_info.registered_device_path|. On success, |error| is set to
152 // base::File::FILE_OK and fills in |file_info|. On failure, |error| is set
153 // to corresponding platform file error and |file_info| is not set.
154 base::File::Error GetFileInfoOnBlockingPoolThread(
155 const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
156 const base::FilePath& file_path,
157 base::File::Info* file_info) {
158 base::ThreadRestrictions::AssertIOAllowed();
159 DCHECK(!device_info.registered_device_path.empty());
160 DCHECK(!file_path.empty());
161 DCHECK(file_info);
162 IPortableDevice* device =
163 PortableDeviceMapService::GetInstance()->GetPortableDevice(
164 device_info.registered_device_path);
165 if (!device)
166 return base::File::FILE_ERROR_FAILED;
168 base::string16 object_id =
169 GetFileObjectIdFromPathOnBlockingPoolThread(device_info, file_path);
170 if (object_id.empty())
171 return base::File::FILE_ERROR_FAILED;
172 return media_transfer_protocol::GetFileEntryInfo(device, object_id,
173 file_info);
176 // Reads the |root| directory file entries on a blocking pool thread. On
177 // success, |error| is set to base::File::FILE_OK and |entries| contains the
178 // directory file entries. On failure, |error| is set to platform file error
179 // and |entries| is not set.
180 base::File::Error ReadDirectoryOnBlockingPoolThread(
181 const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
182 const base::FilePath& root,
183 storage::AsyncFileUtil::EntryList* entries) {
184 base::ThreadRestrictions::AssertIOAllowed();
185 DCHECK(!root.empty());
186 DCHECK(entries);
187 base::File::Info file_info;
188 base::File::Error error = GetFileInfoOnBlockingPoolThread(device_info, root,
189 &file_info);
190 if (error != base::File::FILE_OK)
191 return error;
193 if (!file_info.is_directory)
194 return base::File::FILE_ERROR_NOT_A_DIRECTORY;
196 base::FilePath current;
197 scoped_ptr<MTPDeviceObjectEnumerator> file_enum =
198 CreateFileEnumeratorOnBlockingPoolThread(device_info, root);
199 if (!file_enum)
200 return error;
202 while (!(current = file_enum->Next()).empty()) {
203 storage::DirectoryEntry entry;
204 entry.is_directory = file_enum->IsDirectory();
205 entry.name = storage::VirtualPath::BaseName(current).value();
206 entry.size = file_enum->Size();
207 entry.last_modified_time = file_enum->LastModifiedTime();
208 entries->push_back(entry);
210 return error;
213 // Gets the device file stream object on a blocking pool thread.
214 // |device_info| contains the device storage partition details.
215 // On success, returns base::File::FILE_OK and file stream details are set in
216 // |file_details|. On failure, returns a platform file error and file stream
217 // details are not set in |file_details|.
218 base::File::Error GetFileStreamOnBlockingPoolThread(
219 const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
220 SnapshotFileDetails* file_details) {
221 base::ThreadRestrictions::AssertIOAllowed();
222 DCHECK(file_details);
223 DCHECK(!file_details->request_info().device_file_path.empty());
224 DCHECK(!file_details->request_info().snapshot_file_path.empty());
225 IPortableDevice* device =
226 PortableDeviceMapService::GetInstance()->GetPortableDevice(
227 device_info.registered_device_path);
228 if (!device)
229 return base::File::FILE_ERROR_FAILED;
231 base::string16 file_object_id =
232 GetFileObjectIdFromPathOnBlockingPoolThread(
233 device_info, file_details->request_info().device_file_path);
234 if (file_object_id.empty())
235 return base::File::FILE_ERROR_FAILED;
237 base::File::Info file_info;
238 base::File::Error error =
239 GetFileInfoOnBlockingPoolThread(
240 device_info,
241 file_details->request_info().device_file_path,
242 &file_info);
243 if (error != base::File::FILE_OK)
244 return error;
246 DWORD optimal_transfer_size = 0;
247 base::win::ScopedComPtr<IStream> file_stream;
248 if (file_info.size > 0) {
249 HRESULT hr = media_transfer_protocol::GetFileStreamForObject(
250 device,
251 file_object_id,
252 file_stream.Receive(),
253 &optimal_transfer_size);
254 if (hr != S_OK)
255 return base::File::FILE_ERROR_FAILED;
258 // LocalFileStreamReader is used to read the contents of the snapshot file.
259 // Snapshot file modification time does not match the last modified time
260 // of the original media file. Therefore, set the last modified time to null
261 // in order to avoid the verification in LocalFileStreamReader.
263 // Users will use HTML5 FileSystem Entry getMetadata() interface to get the
264 // actual last modified time of the media file.
265 file_info.last_modified = base::Time();
267 DCHECK(file_info.size == 0 || optimal_transfer_size > 0U);
268 file_details->set_file_info(file_info);
269 file_details->set_device_file_stream(file_stream.get());
270 file_details->set_optimal_transfer_size(optimal_transfer_size);
271 return error;
274 // Copies the data chunk from device file to the snapshot file based on the
275 // parameters specified by |file_details|.
276 // Returns the total number of bytes written to the snapshot file for non-empty
277 // files, or 0 on failure. For empty files, just return 0.
278 DWORD WriteDataChunkIntoSnapshotFileOnBlockingPoolThread(
279 const SnapshotFileDetails& file_details) {
280 base::ThreadRestrictions::AssertIOAllowed();
281 if (file_details.file_info().size == 0)
282 return 0;
283 return media_transfer_protocol::CopyDataChunkToLocalFile(
284 file_details.device_file_stream(),
285 file_details.request_info().snapshot_file_path,
286 file_details.optimal_transfer_size());
289 void DeletePortableDeviceOnBlockingPoolThread(
290 const base::string16& registered_device_path) {
291 base::ThreadRestrictions::AssertIOAllowed();
292 PortableDeviceMapService::GetInstance()->RemovePortableDevice(
293 registered_device_path);
296 } // namespace
298 // Used by CreateMTPDeviceAsyncDelegate() to create the MTP device
299 // delegate on the IO thread.
300 void OnGetStorageInfoCreateDelegate(
301 const base::string16& device_location,
302 const CreateMTPDeviceAsyncDelegateCallback& callback,
303 base::string16* pnp_device_id,
304 base::string16* storage_object_id,
305 bool succeeded) {
306 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
307 DCHECK(pnp_device_id);
308 DCHECK(storage_object_id);
309 if (!succeeded)
310 return;
311 callback.Run(new MTPDeviceDelegateImplWin(device_location,
312 *pnp_device_id,
313 *storage_object_id));
316 void CreateMTPDeviceAsyncDelegate(
317 const base::string16& device_location,
318 const bool read_only,
319 const CreateMTPDeviceAsyncDelegateCallback& callback) {
320 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
322 // Write operation is not supported on Windows.
323 DCHECK(read_only);
325 DCHECK(!device_location.empty());
326 base::string16* pnp_device_id = new base::string16;
327 base::string16* storage_object_id = new base::string16;
328 content::BrowserThread::PostTaskAndReplyWithResult<bool>(
329 content::BrowserThread::UI,
330 FROM_HERE,
331 base::Bind(&GetStorageInfoOnUIThread,
332 device_location,
333 base::Unretained(pnp_device_id),
334 base::Unretained(storage_object_id)),
335 base::Bind(&OnGetStorageInfoCreateDelegate,
336 device_location,
337 callback,
338 base::Owned(pnp_device_id),
339 base::Owned(storage_object_id)));
342 // MTPDeviceDelegateImplWin ---------------------------------------------------
344 MTPDeviceDelegateImplWin::StorageDeviceInfo::StorageDeviceInfo(
345 const base::string16& pnp_device_id,
346 const base::string16& registered_device_path,
347 const base::string16& storage_object_id)
348 : pnp_device_id(pnp_device_id),
349 registered_device_path(registered_device_path),
350 storage_object_id(storage_object_id) {
351 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
354 MTPDeviceDelegateImplWin::PendingTaskInfo::PendingTaskInfo(
355 const tracked_objects::Location& location,
356 const base::Callback<base::File::Error(void)>& task,
357 const base::Callback<void(base::File::Error)>& reply)
358 : location(location),
359 task(task),
360 reply(reply) {
363 MTPDeviceDelegateImplWin::PendingTaskInfo::~PendingTaskInfo() {
366 MTPDeviceDelegateImplWin::MTPDeviceDelegateImplWin(
367 const base::string16& registered_device_path,
368 const base::string16& pnp_device_id,
369 const base::string16& storage_object_id)
370 : init_state_(UNINITIALIZED),
371 media_task_runner_(MediaFileSystemBackend::MediaTaskRunner()),
372 storage_device_info_(pnp_device_id,
373 registered_device_path,
374 storage_object_id),
375 task_in_progress_(false),
376 weak_ptr_factory_(this) {
377 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
378 DCHECK(!registered_device_path.empty());
379 DCHECK(!pnp_device_id.empty());
380 DCHECK(!storage_object_id.empty());
381 DCHECK(media_task_runner_.get());
384 MTPDeviceDelegateImplWin::~MTPDeviceDelegateImplWin() {
385 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
388 void MTPDeviceDelegateImplWin::GetFileInfo(
389 const base::FilePath& file_path,
390 const GetFileInfoSuccessCallback& success_callback,
391 const ErrorCallback& error_callback) {
392 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
393 DCHECK(!file_path.empty());
394 base::File::Info* file_info = new base::File::Info;
395 EnsureInitAndRunTask(
396 PendingTaskInfo(FROM_HERE,
397 base::Bind(&GetFileInfoOnBlockingPoolThread,
398 storage_device_info_,
399 file_path,
400 base::Unretained(file_info)),
401 base::Bind(&MTPDeviceDelegateImplWin::OnGetFileInfo,
402 weak_ptr_factory_.GetWeakPtr(),
403 success_callback,
404 error_callback,
405 base::Owned(file_info))));
408 void MTPDeviceDelegateImplWin::CreateDirectory(
409 const base::FilePath& directory_path,
410 const bool exclusive,
411 const bool recursive,
412 const CreateDirectorySuccessCallback& success_callback,
413 const ErrorCallback& error_callback) {
414 NOTREACHED();
417 void MTPDeviceDelegateImplWin::ReadDirectory(
418 const base::FilePath& root,
419 const ReadDirectorySuccessCallback& success_callback,
420 const ErrorCallback& error_callback) {
421 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
422 DCHECK(!root.empty());
423 storage::AsyncFileUtil::EntryList* entries =
424 new storage::AsyncFileUtil::EntryList;
425 EnsureInitAndRunTask(
426 PendingTaskInfo(FROM_HERE,
427 base::Bind(&ReadDirectoryOnBlockingPoolThread,
428 storage_device_info_,
429 root,
430 base::Unretained(entries)),
431 base::Bind(&MTPDeviceDelegateImplWin::OnDidReadDirectory,
432 weak_ptr_factory_.GetWeakPtr(),
433 success_callback,
434 error_callback,
435 base::Owned(entries))));
438 void MTPDeviceDelegateImplWin::CreateSnapshotFile(
439 const base::FilePath& device_file_path,
440 const base::FilePath& snapshot_file_path,
441 const CreateSnapshotFileSuccessCallback& success_callback,
442 const ErrorCallback& error_callback) {
443 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
444 DCHECK(!device_file_path.empty());
445 DCHECK(!snapshot_file_path.empty());
446 scoped_ptr<SnapshotFileDetails> file_details(
447 new SnapshotFileDetails(SnapshotRequestInfo(device_file_path,
448 snapshot_file_path,
449 success_callback,
450 error_callback)));
451 // Passing a raw SnapshotFileDetails* to the blocking pool is safe, because
452 // it is owned by |file_details| in the reply callback.
453 EnsureInitAndRunTask(
454 PendingTaskInfo(FROM_HERE,
455 base::Bind(&GetFileStreamOnBlockingPoolThread,
456 storage_device_info_,
457 file_details.get()),
458 base::Bind(&MTPDeviceDelegateImplWin::OnGetFileStream,
459 weak_ptr_factory_.GetWeakPtr(),
460 base::Passed(&file_details))));
463 bool MTPDeviceDelegateImplWin::IsStreaming() {
464 return false;
467 void MTPDeviceDelegateImplWin::ReadBytes(
468 const base::FilePath& device_file_path,
469 const scoped_refptr<net::IOBuffer>& buf,
470 int64 offset,
471 int buf_len,
472 const ReadBytesSuccessCallback& success_callback,
473 const ErrorCallback& error_callback) {
474 NOTREACHED();
477 bool MTPDeviceDelegateImplWin::IsReadOnly() const {
478 return true;
481 void MTPDeviceDelegateImplWin::CopyFileLocal(
482 const base::FilePath& source_file_path,
483 const base::FilePath& device_file_path,
484 const CreateTemporaryFileCallback& create_temporary_file_callback,
485 const CopyFileProgressCallback& progress_callback,
486 const CopyFileLocalSuccessCallback& success_callback,
487 const ErrorCallback& error_callback) {
488 NOTREACHED();
491 void MTPDeviceDelegateImplWin::MoveFileLocal(
492 const base::FilePath& source_file_path,
493 const base::FilePath& device_file_path,
494 const CreateTemporaryFileCallback& create_temporary_file_callback,
495 const MoveFileLocalSuccessCallback& success_callback,
496 const ErrorCallback& error_callback) {
497 NOTREACHED();
500 void MTPDeviceDelegateImplWin::CopyFileFromLocal(
501 const base::FilePath& source_file_path,
502 const base::FilePath& device_file_path,
503 const CopyFileFromLocalSuccessCallback& success_callback,
504 const ErrorCallback& error_callback) {
505 NOTREACHED();
508 void MTPDeviceDelegateImplWin::DeleteFile(
509 const base::FilePath& file_path,
510 const DeleteFileSuccessCallback& success_callback,
511 const ErrorCallback& error_callback) {
512 NOTREACHED();
515 void MTPDeviceDelegateImplWin::DeleteDirectory(
516 const base::FilePath& file_path,
517 const DeleteDirectorySuccessCallback& success_callback,
518 const ErrorCallback& error_callback) {
519 NOTREACHED();
522 void MTPDeviceDelegateImplWin::AddWatcher(
523 const GURL& origin,
524 const base::FilePath& file_path,
525 const bool recursive,
526 const storage::WatcherManager::StatusCallback& callback,
527 const storage::WatcherManager::NotificationCallback&
528 notification_callback) {
529 NOTIMPLEMENTED();
530 callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
533 void MTPDeviceDelegateImplWin::RemoveWatcher(
534 const GURL& origin,
535 const base::FilePath& file_path,
536 const bool recursive,
537 const storage::WatcherManager::StatusCallback& callback) {
538 NOTIMPLEMENTED();
539 callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
542 void MTPDeviceDelegateImplWin::CancelPendingTasksAndDeleteDelegate() {
543 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
544 PortableDeviceMapService::GetInstance()->MarkPortableDeviceForDeletion(
545 storage_device_info_.registered_device_path);
546 media_task_runner_->PostTask(
547 FROM_HERE,
548 base::Bind(&DeletePortableDeviceOnBlockingPoolThread,
549 storage_device_info_.registered_device_path));
550 while (!pending_tasks_.empty())
551 pending_tasks_.pop();
552 delete this;
555 void MTPDeviceDelegateImplWin::EnsureInitAndRunTask(
556 const PendingTaskInfo& task_info) {
557 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
558 if ((init_state_ == INITIALIZED) && !task_in_progress_) {
559 DCHECK(pending_tasks_.empty());
560 DCHECK(!current_snapshot_details_.get());
561 base::PostTaskAndReplyWithResult(media_task_runner_.get(),
562 task_info.location, task_info.task,
563 task_info.reply);
564 task_in_progress_ = true;
565 return;
568 pending_tasks_.push(task_info);
569 if (init_state_ == UNINITIALIZED) {
570 init_state_ = PENDING_INIT;
571 base::PostTaskAndReplyWithResult(
572 media_task_runner_.get(), FROM_HERE,
573 base::Bind(&OpenDeviceOnBlockingPoolThread,
574 storage_device_info_.pnp_device_id,
575 storage_device_info_.registered_device_path),
576 base::Bind(&MTPDeviceDelegateImplWin::OnInitCompleted,
577 weak_ptr_factory_.GetWeakPtr()));
578 task_in_progress_ = true;
582 void MTPDeviceDelegateImplWin::WriteDataChunkIntoSnapshotFile() {
583 DCHECK(current_snapshot_details_.get());
584 base::PostTaskAndReplyWithResult(
585 media_task_runner_.get(), FROM_HERE,
586 base::Bind(&WriteDataChunkIntoSnapshotFileOnBlockingPoolThread,
587 *current_snapshot_details_),
588 base::Bind(&MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile,
589 weak_ptr_factory_.GetWeakPtr(),
590 current_snapshot_details_->request_info().snapshot_file_path));
593 void MTPDeviceDelegateImplWin::ProcessNextPendingRequest() {
594 DCHECK(!task_in_progress_);
595 if (pending_tasks_.empty())
596 return;
597 const PendingTaskInfo& task_info = pending_tasks_.front();
598 task_in_progress_ = true;
599 base::PostTaskAndReplyWithResult(media_task_runner_.get(), task_info.location,
600 task_info.task, task_info.reply);
601 pending_tasks_.pop();
604 void MTPDeviceDelegateImplWin::OnInitCompleted(bool succeeded) {
605 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
606 init_state_ = succeeded ? INITIALIZED : UNINITIALIZED;
607 task_in_progress_ = false;
608 ProcessNextPendingRequest();
611 void MTPDeviceDelegateImplWin::OnGetFileInfo(
612 const GetFileInfoSuccessCallback& success_callback,
613 const ErrorCallback& error_callback,
614 base::File::Info* file_info,
615 base::File::Error error) {
616 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
617 DCHECK(file_info);
618 if (error == base::File::FILE_OK)
619 success_callback.Run(*file_info);
620 else
621 error_callback.Run(error);
622 task_in_progress_ = false;
623 ProcessNextPendingRequest();
626 void MTPDeviceDelegateImplWin::OnDidReadDirectory(
627 const ReadDirectorySuccessCallback& success_callback,
628 const ErrorCallback& error_callback,
629 storage::AsyncFileUtil::EntryList* file_list,
630 base::File::Error error) {
631 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
632 DCHECK(file_list);
633 if (error == base::File::FILE_OK)
634 success_callback.Run(*file_list, false /*no more entries*/);
635 else
636 error_callback.Run(error);
637 task_in_progress_ = false;
638 ProcessNextPendingRequest();
641 void MTPDeviceDelegateImplWin::OnGetFileStream(
642 scoped_ptr<SnapshotFileDetails> file_details,
643 base::File::Error error) {
644 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
645 DCHECK(file_details);
646 DCHECK(!file_details->request_info().device_file_path.empty());
647 DCHECK(!file_details->request_info().snapshot_file_path.empty());
648 DCHECK(!current_snapshot_details_.get());
649 if (error != base::File::FILE_OK) {
650 file_details->request_info().error_callback.Run(error);
651 task_in_progress_ = false;
652 ProcessNextPendingRequest();
653 return;
655 DCHECK(file_details->file_info().size == 0 ||
656 file_details->device_file_stream());
657 current_snapshot_details_.reset(file_details.release());
658 WriteDataChunkIntoSnapshotFile();
661 void MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile(
662 const base::FilePath& snapshot_file_path,
663 DWORD bytes_written) {
664 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
665 DCHECK(!snapshot_file_path.empty());
666 if (!current_snapshot_details_.get())
667 return;
668 DCHECK_EQ(
669 current_snapshot_details_->request_info().snapshot_file_path.value(),
670 snapshot_file_path.value());
672 bool succeeded = false;
673 bool should_continue = false;
674 if (current_snapshot_details_->file_info().size > 0) {
675 if (current_snapshot_details_->AddBytesWritten(bytes_written)) {
676 if (current_snapshot_details_->IsSnapshotFileWriteComplete()) {
677 succeeded = true;
678 } else {
679 should_continue = true;
682 } else {
683 // Handle empty files.
684 DCHECK_EQ(0U, bytes_written);
685 succeeded = true;
688 if (should_continue) {
689 WriteDataChunkIntoSnapshotFile();
690 return;
692 if (succeeded) {
693 current_snapshot_details_->request_info().success_callback.Run(
694 current_snapshot_details_->file_info(),
695 current_snapshot_details_->request_info().snapshot_file_path);
696 } else {
697 current_snapshot_details_->request_info().error_callback.Run(
698 base::File::FILE_ERROR_FAILED);
700 task_in_progress_ = false;
701 current_snapshot_details_.reset();
702 ProcessNextPendingRequest();