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.
5 // MTPDeviceDelegateImplWin implementation.
7 #include "chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.h"
9 #include <portabledevice.h>
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"
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();
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
);
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
) {
91 media_transfer_protocol::GetObjectIdFromName(device
, parent_id
,
93 if (file_object_id
.empty())
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
);
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
,
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
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());
162 IPortableDevice
* device
=
163 PortableDeviceMapService::GetInstance()->GetPortableDevice(
164 device_info
.registered_device_path
);
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
,
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());
187 base::File::Info file_info
;
188 base::File::Error error
= GetFileInfoOnBlockingPoolThread(device_info
, root
,
190 if (error
!= base::File::FILE_OK
)
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
);
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
);
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
);
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(
241 file_details
->request_info().device_file_path
,
243 if (error
!= base::File::FILE_OK
)
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(
252 file_stream
.Receive(),
253 &optimal_transfer_size
);
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
);
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)
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
);
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
,
306 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
307 DCHECK(pnp_device_id
);
308 DCHECK(storage_object_id
);
311 callback
.Run(new MTPDeviceDelegateImplWin(device_location
,
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.
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
,
331 base::Bind(&GetStorageInfoOnUIThread
,
333 base::Unretained(pnp_device_id
),
334 base::Unretained(storage_object_id
)),
335 base::Bind(&OnGetStorageInfoCreateDelegate
,
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
),
363 MTPDeviceDelegateImplWin::MTPDeviceDelegateImplWin(
364 const base::string16
& registered_device_path
,
365 const base::string16
& pnp_device_id
,
366 const base::string16
& storage_object_id
)
367 : storage_device_info_(pnp_device_id
, registered_device_path
,
369 init_state_(UNINITIALIZED
),
370 media_task_runner_(MediaFileSystemBackend::MediaTaskRunner()),
371 task_in_progress_(false),
372 weak_ptr_factory_(this) {
373 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
374 DCHECK(!registered_device_path
.empty());
375 DCHECK(!pnp_device_id
.empty());
376 DCHECK(!storage_object_id
.empty());
377 DCHECK(media_task_runner_
.get());
380 MTPDeviceDelegateImplWin::~MTPDeviceDelegateImplWin() {
381 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
384 void MTPDeviceDelegateImplWin::GetFileInfo(
385 const base::FilePath
& file_path
,
386 const GetFileInfoSuccessCallback
& success_callback
,
387 const ErrorCallback
& error_callback
) {
388 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
389 DCHECK(!file_path
.empty());
390 base::File::Info
* file_info
= new base::File::Info
;
391 EnsureInitAndRunTask(
392 PendingTaskInfo(FROM_HERE
,
393 base::Bind(&GetFileInfoOnBlockingPoolThread
,
394 storage_device_info_
,
396 base::Unretained(file_info
)),
397 base::Bind(&MTPDeviceDelegateImplWin::OnGetFileInfo
,
398 weak_ptr_factory_
.GetWeakPtr(),
401 base::Owned(file_info
))));
404 void MTPDeviceDelegateImplWin::CreateDirectory(
405 const base::FilePath
& directory_path
,
406 const bool exclusive
,
407 const bool recursive
,
408 const CreateDirectorySuccessCallback
& success_callback
,
409 const ErrorCallback
& error_callback
) {
413 void MTPDeviceDelegateImplWin::ReadDirectory(
414 const base::FilePath
& root
,
415 const ReadDirectorySuccessCallback
& success_callback
,
416 const ErrorCallback
& error_callback
) {
417 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
418 DCHECK(!root
.empty());
419 storage::AsyncFileUtil::EntryList
* entries
=
420 new storage::AsyncFileUtil::EntryList
;
421 EnsureInitAndRunTask(
422 PendingTaskInfo(FROM_HERE
,
423 base::Bind(&ReadDirectoryOnBlockingPoolThread
,
424 storage_device_info_
,
426 base::Unretained(entries
)),
427 base::Bind(&MTPDeviceDelegateImplWin::OnDidReadDirectory
,
428 weak_ptr_factory_
.GetWeakPtr(),
431 base::Owned(entries
))));
434 void MTPDeviceDelegateImplWin::CreateSnapshotFile(
435 const base::FilePath
& device_file_path
,
436 const base::FilePath
& snapshot_file_path
,
437 const CreateSnapshotFileSuccessCallback
& success_callback
,
438 const ErrorCallback
& error_callback
) {
439 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
440 DCHECK(!device_file_path
.empty());
441 DCHECK(!snapshot_file_path
.empty());
442 scoped_ptr
<SnapshotFileDetails
> file_details(
443 new SnapshotFileDetails(SnapshotRequestInfo(device_file_path
,
447 // Passing a raw SnapshotFileDetails* to the blocking pool is safe, because
448 // it is owned by |file_details| in the reply callback.
449 EnsureInitAndRunTask(
450 PendingTaskInfo(FROM_HERE
,
451 base::Bind(&GetFileStreamOnBlockingPoolThread
,
452 storage_device_info_
,
454 base::Bind(&MTPDeviceDelegateImplWin::OnGetFileStream
,
455 weak_ptr_factory_
.GetWeakPtr(),
456 base::Passed(&file_details
))));
459 bool MTPDeviceDelegateImplWin::IsStreaming() {
463 void MTPDeviceDelegateImplWin::ReadBytes(
464 const base::FilePath
& device_file_path
,
465 const scoped_refptr
<net::IOBuffer
>& buf
,
468 const ReadBytesSuccessCallback
& success_callback
,
469 const ErrorCallback
& error_callback
) {
473 bool MTPDeviceDelegateImplWin::IsReadOnly() const {
477 void MTPDeviceDelegateImplWin::CopyFileLocal(
478 const base::FilePath
& source_file_path
,
479 const base::FilePath
& device_file_path
,
480 const CreateTemporaryFileCallback
& create_temporary_file_callback
,
481 const CopyFileProgressCallback
& progress_callback
,
482 const CopyFileLocalSuccessCallback
& success_callback
,
483 const ErrorCallback
& error_callback
) {
487 void MTPDeviceDelegateImplWin::MoveFileLocal(
488 const base::FilePath
& source_file_path
,
489 const base::FilePath
& device_file_path
,
490 const CreateTemporaryFileCallback
& create_temporary_file_callback
,
491 const MoveFileLocalSuccessCallback
& success_callback
,
492 const ErrorCallback
& error_callback
) {
496 void MTPDeviceDelegateImplWin::CopyFileFromLocal(
497 const base::FilePath
& source_file_path
,
498 const base::FilePath
& device_file_path
,
499 const CopyFileFromLocalSuccessCallback
& success_callback
,
500 const ErrorCallback
& error_callback
) {
504 void MTPDeviceDelegateImplWin::DeleteFile(
505 const base::FilePath
& file_path
,
506 const DeleteFileSuccessCallback
& success_callback
,
507 const ErrorCallback
& error_callback
) {
511 void MTPDeviceDelegateImplWin::DeleteDirectory(
512 const base::FilePath
& file_path
,
513 const DeleteDirectorySuccessCallback
& success_callback
,
514 const ErrorCallback
& error_callback
) {
518 void MTPDeviceDelegateImplWin::CancelPendingTasksAndDeleteDelegate() {
519 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
520 PortableDeviceMapService::GetInstance()->MarkPortableDeviceForDeletion(
521 storage_device_info_
.registered_device_path
);
522 media_task_runner_
->PostTask(
524 base::Bind(&DeletePortableDeviceOnBlockingPoolThread
,
525 storage_device_info_
.registered_device_path
));
526 while (!pending_tasks_
.empty())
527 pending_tasks_
.pop();
531 void MTPDeviceDelegateImplWin::EnsureInitAndRunTask(
532 const PendingTaskInfo
& task_info
) {
533 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
534 if ((init_state_
== INITIALIZED
) && !task_in_progress_
) {
535 DCHECK(pending_tasks_
.empty());
536 DCHECK(!current_snapshot_details_
.get());
537 base::PostTaskAndReplyWithResult(media_task_runner_
.get(),
538 task_info
.location
, task_info
.task
,
540 task_in_progress_
= true;
544 pending_tasks_
.push(task_info
);
545 if (init_state_
== UNINITIALIZED
) {
546 init_state_
= PENDING_INIT
;
547 base::PostTaskAndReplyWithResult(
548 media_task_runner_
.get(), FROM_HERE
,
549 base::Bind(&OpenDeviceOnBlockingPoolThread
,
550 storage_device_info_
.pnp_device_id
,
551 storage_device_info_
.registered_device_path
),
552 base::Bind(&MTPDeviceDelegateImplWin::OnInitCompleted
,
553 weak_ptr_factory_
.GetWeakPtr()));
554 task_in_progress_
= true;
558 void MTPDeviceDelegateImplWin::WriteDataChunkIntoSnapshotFile() {
559 DCHECK(current_snapshot_details_
.get());
560 base::PostTaskAndReplyWithResult(
561 media_task_runner_
.get(), FROM_HERE
,
562 base::Bind(&WriteDataChunkIntoSnapshotFileOnBlockingPoolThread
,
563 *current_snapshot_details_
),
564 base::Bind(&MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile
,
565 weak_ptr_factory_
.GetWeakPtr(),
566 current_snapshot_details_
->request_info().snapshot_file_path
));
569 void MTPDeviceDelegateImplWin::ProcessNextPendingRequest() {
570 DCHECK(!task_in_progress_
);
571 if (pending_tasks_
.empty())
573 const PendingTaskInfo
& task_info
= pending_tasks_
.front();
574 task_in_progress_
= true;
575 base::PostTaskAndReplyWithResult(media_task_runner_
.get(), task_info
.location
,
576 task_info
.task
, task_info
.reply
);
577 pending_tasks_
.pop();
580 void MTPDeviceDelegateImplWin::OnInitCompleted(bool succeeded
) {
581 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
582 init_state_
= succeeded
? INITIALIZED
: UNINITIALIZED
;
583 task_in_progress_
= false;
584 ProcessNextPendingRequest();
587 void MTPDeviceDelegateImplWin::OnGetFileInfo(
588 const GetFileInfoSuccessCallback
& success_callback
,
589 const ErrorCallback
& error_callback
,
590 base::File::Info
* file_info
,
591 base::File::Error error
) {
592 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
594 if (error
== base::File::FILE_OK
)
595 success_callback
.Run(*file_info
);
597 error_callback
.Run(error
);
598 task_in_progress_
= false;
599 ProcessNextPendingRequest();
602 void MTPDeviceDelegateImplWin::OnDidReadDirectory(
603 const ReadDirectorySuccessCallback
& success_callback
,
604 const ErrorCallback
& error_callback
,
605 storage::AsyncFileUtil::EntryList
* file_list
,
606 base::File::Error error
) {
607 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
609 if (error
== base::File::FILE_OK
)
610 success_callback
.Run(*file_list
, false /*no more entries*/);
612 error_callback
.Run(error
);
613 task_in_progress_
= false;
614 ProcessNextPendingRequest();
617 void MTPDeviceDelegateImplWin::OnGetFileStream(
618 scoped_ptr
<SnapshotFileDetails
> file_details
,
619 base::File::Error error
) {
620 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
621 DCHECK(file_details
);
622 DCHECK(!file_details
->request_info().device_file_path
.empty());
623 DCHECK(!file_details
->request_info().snapshot_file_path
.empty());
624 DCHECK(!current_snapshot_details_
.get());
625 if (error
!= base::File::FILE_OK
) {
626 file_details
->request_info().error_callback
.Run(error
);
627 task_in_progress_
= false;
628 ProcessNextPendingRequest();
631 DCHECK(file_details
->file_info().size
== 0 ||
632 file_details
->device_file_stream());
633 current_snapshot_details_
.reset(file_details
.release());
634 WriteDataChunkIntoSnapshotFile();
637 void MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile(
638 const base::FilePath
& snapshot_file_path
,
639 DWORD bytes_written
) {
640 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
641 DCHECK(!snapshot_file_path
.empty());
642 if (!current_snapshot_details_
.get())
645 current_snapshot_details_
->request_info().snapshot_file_path
.value(),
646 snapshot_file_path
.value());
648 bool succeeded
= false;
649 bool should_continue
= false;
650 if (current_snapshot_details_
->file_info().size
> 0) {
651 if (current_snapshot_details_
->AddBytesWritten(bytes_written
)) {
652 if (current_snapshot_details_
->IsSnapshotFileWriteComplete()) {
655 should_continue
= true;
659 // Handle empty files.
660 DCHECK_EQ(0U, bytes_written
);
664 if (should_continue
) {
665 WriteDataChunkIntoSnapshotFile();
669 current_snapshot_details_
->request_info().success_callback
.Run(
670 current_snapshot_details_
->file_info(),
671 current_snapshot_details_
->request_info().snapshot_file_path
);
673 current_snapshot_details_
->request_info().error_callback
.Run(
674 base::File::FILE_ERROR_FAILED
);
676 task_in_progress_
= false;
677 current_snapshot_details_
.reset();
678 ProcessNextPendingRequest();