Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / media_galleries / win / mtp_device_delegate_impl_win.cc
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.
6
7 #include "chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.h"
8
9 #include <portabledevice.h>
10
11 #include <vector>
12
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 "webkit/common/fileapi/file_system_util.h"
33
34 namespace {
35
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(content::BrowserThread::CurrentlyOn(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   StorageMonitor* monitor = StorageMonitor::GetInstance();
51   DCHECK(monitor);
52   return monitor->GetMTPStorageInfoFromDeviceId(
53       base::UTF16ToUTF8(storage_device_id), pnp_device_id, storage_object_id);
54 }
55
56 // Returns the object id of the file object specified by the |file_path|,
57 // e.g. if the |file_path| is "\\MTP:StorageSerial:SID-{1001,,192}:125\DCIM"
58 // and |device_info.registered_device_path_| is
59 // "\\MTP:StorageSerial:SID-{1001,,192}:125", this function returns the
60 // identifier of the "DCIM" folder object.
61 //
62 // Returns an empty string if the device is detached while the request is in
63 // progress or when the |file_path| is invalid.
64 base::string16 GetFileObjectIdFromPathOnBlockingPoolThread(
65     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
66     const base::FilePath& file_path) {
67   base::ThreadRestrictions::AssertIOAllowed();
68   DCHECK(!file_path.empty());
69   IPortableDevice* device =
70       PortableDeviceMapService::GetInstance()->GetPortableDevice(
71           device_info.registered_device_path);
72   if (!device)
73     return base::string16();
74
75   if (device_info.registered_device_path == file_path.value())
76     return device_info.storage_object_id;
77
78   base::FilePath relative_path;
79   if (!base::FilePath(device_info.registered_device_path).AppendRelativePath(
80           file_path, &relative_path))
81     return base::string16();
82
83   std::vector<base::string16> path_components;
84   relative_path.GetComponents(&path_components);
85   DCHECK(!path_components.empty());
86   base::string16 parent_id(device_info.storage_object_id);
87   base::string16 file_object_id;
88   for (size_t i = 0; i < path_components.size(); ++i) {
89     file_object_id =
90         media_transfer_protocol::GetObjectIdFromName(device, parent_id,
91                                                      path_components[i]);
92     if (file_object_id.empty())
93       break;
94     parent_id = file_object_id;
95   }
96   return file_object_id;
97 }
98
99 // Returns a pointer to a new instance of AbstractFileEnumerator for the given
100 // |root| directory. Called on a blocking pool thread.
101 scoped_ptr<MTPDeviceObjectEnumerator>
102 CreateFileEnumeratorOnBlockingPoolThread(
103     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
104     const base::FilePath& root) {
105   base::ThreadRestrictions::AssertIOAllowed();
106   DCHECK(!device_info.registered_device_path.empty());
107   DCHECK(!root.empty());
108   IPortableDevice* device =
109       PortableDeviceMapService::GetInstance()->GetPortableDevice(
110           device_info.registered_device_path);
111   if (!device)
112     return scoped_ptr<MTPDeviceObjectEnumerator>();
113
114   base::string16 object_id =
115       GetFileObjectIdFromPathOnBlockingPoolThread(device_info, root);
116   if (object_id.empty())
117     return scoped_ptr<MTPDeviceObjectEnumerator>();
118
119   MTPDeviceObjectEntries entries;
120   if (!media_transfer_protocol::GetDirectoryEntries(device, object_id,
121                                                     &entries) ||
122       entries.empty())
123     return scoped_ptr<MTPDeviceObjectEnumerator>();
124
125   return scoped_ptr<MTPDeviceObjectEnumerator>(
126       new MTPDeviceObjectEnumerator(entries));
127 }
128
129 // Opens the device for communication on a blocking pool thread.
130 // |pnp_device_id| specifies the PnP device id.
131 // |registered_device_path| specifies the registered file system root path for
132 // the given device.
133 bool OpenDeviceOnBlockingPoolThread(
134     const base::string16& pnp_device_id,
135     const base::string16& registered_device_path) {
136   base::ThreadRestrictions::AssertIOAllowed();
137   DCHECK(!pnp_device_id.empty());
138   DCHECK(!registered_device_path.empty());
139   base::win::ScopedComPtr<IPortableDevice> device =
140       media_transfer_protocol::OpenDevice(pnp_device_id);
141   bool init_succeeded = device.get() != NULL;
142   if (init_succeeded) {
143     PortableDeviceMapService::GetInstance()->AddPortableDevice(
144         registered_device_path, device.get());
145   }
146   return init_succeeded;
147 }
148
149 // Gets the |file_path| details from the MTP device specified by the
150 // |device_info.registered_device_path|. On success, |error| is set to
151 // base::File::FILE_OK and fills in |file_info|. On failure, |error| is set
152 // to corresponding platform file error and |file_info| is not set.
153 base::File::Error GetFileInfoOnBlockingPoolThread(
154     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
155     const base::FilePath& file_path,
156     base::File::Info* file_info) {
157   base::ThreadRestrictions::AssertIOAllowed();
158   DCHECK(!device_info.registered_device_path.empty());
159   DCHECK(!file_path.empty());
160   DCHECK(file_info);
161   IPortableDevice* device =
162       PortableDeviceMapService::GetInstance()->GetPortableDevice(
163           device_info.registered_device_path);
164   if (!device)
165     return base::File::FILE_ERROR_FAILED;
166
167   base::string16 object_id =
168       GetFileObjectIdFromPathOnBlockingPoolThread(device_info, file_path);
169   if (object_id.empty())
170     return base::File::FILE_ERROR_FAILED;
171   return media_transfer_protocol::GetFileEntryInfo(device, object_id,
172                                                    file_info);
173 }
174
175 // Reads the |root| directory file entries on a blocking pool thread. On
176 // success, |error| is set to base::File::FILE_OK and |entries| contains the
177 // directory file entries. On failure, |error| is set to platform file error
178 // and |entries| is not set.
179 base::File::Error ReadDirectoryOnBlockingPoolThread(
180     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
181     const base::FilePath& root,
182     fileapi::AsyncFileUtil::EntryList* entries) {
183   base::ThreadRestrictions::AssertIOAllowed();
184   DCHECK(!root.empty());
185   DCHECK(entries);
186   base::File::Info file_info;
187   base::File::Error error = GetFileInfoOnBlockingPoolThread(device_info, root,
188                                                             &file_info);
189   if (error != base::File::FILE_OK)
190     return error;
191
192   if (!file_info.is_directory)
193     return base::File::FILE_ERROR_NOT_A_DIRECTORY;
194
195   base::FilePath current;
196   scoped_ptr<MTPDeviceObjectEnumerator> file_enum =
197       CreateFileEnumeratorOnBlockingPoolThread(device_info, root);
198   if (!file_enum)
199     return error;
200
201   while (!(current = file_enum->Next()).empty()) {
202     fileapi::DirectoryEntry entry;
203     entry.is_directory = file_enum->IsDirectory();
204     entry.name = fileapi::VirtualPath::BaseName(current).value();
205     entry.size = file_enum->Size();
206     entry.last_modified_time = file_enum->LastModifiedTime();
207     entries->push_back(entry);
208   }
209   return error;
210 }
211
212 // Gets the device file stream object on a blocking pool thread.
213 // |device_info| contains the device storage partition details.
214 // On success, returns base::File::FILE_OK and file stream details are set in
215 // |file_details|. On failure, returns a platform file error and file stream
216 // details are not set in |file_details|.
217 base::File::Error GetFileStreamOnBlockingPoolThread(
218     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
219     SnapshotFileDetails* file_details) {
220   base::ThreadRestrictions::AssertIOAllowed();
221   DCHECK(file_details);
222   DCHECK(!file_details->request_info().device_file_path.empty());
223   DCHECK(!file_details->request_info().snapshot_file_path.empty());
224   IPortableDevice* device =
225       PortableDeviceMapService::GetInstance()->GetPortableDevice(
226           device_info.registered_device_path);
227   if (!device)
228     return base::File::FILE_ERROR_FAILED;
229
230   base::string16 file_object_id =
231       GetFileObjectIdFromPathOnBlockingPoolThread(
232           device_info, file_details->request_info().device_file_path);
233   if (file_object_id.empty())
234     return base::File::FILE_ERROR_FAILED;
235
236   base::File::Info file_info;
237   base::File::Error error =
238       GetFileInfoOnBlockingPoolThread(
239           device_info,
240           file_details->request_info().device_file_path,
241           &file_info);
242   if (error != base::File::FILE_OK)
243     return error;
244
245   DWORD optimal_transfer_size = 0;
246   base::win::ScopedComPtr<IStream> file_stream;
247   if (file_info.size > 0) {
248     HRESULT hr = media_transfer_protocol::GetFileStreamForObject(
249         device,
250         file_object_id,
251         file_stream.Receive(),
252         &optimal_transfer_size);
253     if (hr != S_OK)
254       return base::File::FILE_ERROR_FAILED;
255   }
256
257   // LocalFileStreamReader is used to read the contents of the snapshot file.
258   // Snapshot file modification time does not match the last modified time
259   // of the original media file. Therefore, set the last modified time to null
260   // in order to avoid the verification in LocalFileStreamReader.
261   //
262   // Users will use HTML5 FileSystem Entry getMetadata() interface to get the
263   // actual last modified time of the media file.
264   file_info.last_modified = base::Time();
265
266   DCHECK(file_info.size == 0 || optimal_transfer_size > 0U);
267   file_details->set_file_info(file_info);
268   file_details->set_device_file_stream(file_stream);
269   file_details->set_optimal_transfer_size(optimal_transfer_size);
270   return error;
271 }
272
273 // Copies the data chunk from device file to the snapshot file based on the
274 // parameters specified by |file_details|.
275 // Returns the total number of bytes written to the snapshot file for non-empty
276 // files, or 0 on failure. For empty files, just return 0.
277 DWORD WriteDataChunkIntoSnapshotFileOnBlockingPoolThread(
278     const SnapshotFileDetails& file_details) {
279   base::ThreadRestrictions::AssertIOAllowed();
280   if (file_details.file_info().size == 0)
281     return 0;
282   return media_transfer_protocol::CopyDataChunkToLocalFile(
283       file_details.device_file_stream(),
284       file_details.request_info().snapshot_file_path,
285       file_details.optimal_transfer_size());
286 }
287
288 void DeletePortableDeviceOnBlockingPoolThread(
289     const base::string16& registered_device_path) {
290   base::ThreadRestrictions::AssertIOAllowed();
291   PortableDeviceMapService::GetInstance()->RemovePortableDevice(
292       registered_device_path);
293 }
294
295 }  // namespace
296
297 // Used by CreateMTPDeviceAsyncDelegate() to create the MTP device
298 // delegate on the IO thread.
299 void OnGetStorageInfoCreateDelegate(
300     const base::string16& device_location,
301     const CreateMTPDeviceAsyncDelegateCallback& callback,
302     base::string16* pnp_device_id,
303     base::string16* storage_object_id,
304     bool succeeded) {
305   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
306   DCHECK(pnp_device_id);
307   DCHECK(storage_object_id);
308   if (!succeeded)
309     return;
310   callback.Run(new MTPDeviceDelegateImplWin(device_location,
311                                             *pnp_device_id,
312                                             *storage_object_id));
313 }
314
315 void CreateMTPDeviceAsyncDelegate(
316     const base::string16& device_location,
317     const CreateMTPDeviceAsyncDelegateCallback& callback) {
318   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
319   DCHECK(!device_location.empty());
320   base::string16* pnp_device_id = new base::string16;
321   base::string16* storage_object_id = new base::string16;
322   content::BrowserThread::PostTaskAndReplyWithResult<bool>(
323       content::BrowserThread::UI,
324       FROM_HERE,
325       base::Bind(&GetStorageInfoOnUIThread,
326                  device_location,
327                  base::Unretained(pnp_device_id),
328                  base::Unretained(storage_object_id)),
329       base::Bind(&OnGetStorageInfoCreateDelegate,
330                  device_location,
331                  callback,
332                  base::Owned(pnp_device_id),
333                  base::Owned(storage_object_id)));
334 }
335
336 // MTPDeviceDelegateImplWin ---------------------------------------------------
337
338 MTPDeviceDelegateImplWin::StorageDeviceInfo::StorageDeviceInfo(
339     const base::string16& pnp_device_id,
340     const base::string16& registered_device_path,
341     const base::string16& storage_object_id)
342     : pnp_device_id(pnp_device_id),
343       registered_device_path(registered_device_path),
344       storage_object_id(storage_object_id) {
345   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
346 }
347
348 MTPDeviceDelegateImplWin::PendingTaskInfo::PendingTaskInfo(
349     const tracked_objects::Location& location,
350     const base::Callback<base::File::Error(void)>& task,
351     const base::Callback<void(base::File::Error)>& reply)
352     : location(location),
353       task(task),
354       reply(reply) {
355 }
356
357 MTPDeviceDelegateImplWin::MTPDeviceDelegateImplWin(
358     const base::string16& registered_device_path,
359     const base::string16& pnp_device_id,
360     const base::string16& storage_object_id)
361     : storage_device_info_(pnp_device_id, registered_device_path,
362                            storage_object_id),
363       init_state_(UNINITIALIZED),
364       media_task_runner_(MediaFileSystemBackend::MediaTaskRunner()),
365       task_in_progress_(false),
366       weak_ptr_factory_(this) {
367   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
368   DCHECK(!registered_device_path.empty());
369   DCHECK(!pnp_device_id.empty());
370   DCHECK(!storage_object_id.empty());
371   DCHECK(media_task_runner_.get());
372 }
373
374 MTPDeviceDelegateImplWin::~MTPDeviceDelegateImplWin() {
375   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
376 }
377
378 void MTPDeviceDelegateImplWin::GetFileInfo(
379     const base::FilePath& file_path,
380     const GetFileInfoSuccessCallback& success_callback,
381     const ErrorCallback& error_callback) {
382   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
383   DCHECK(!file_path.empty());
384   base::File::Info* file_info = new base::File::Info;
385   EnsureInitAndRunTask(
386       PendingTaskInfo(FROM_HERE,
387                       base::Bind(&GetFileInfoOnBlockingPoolThread,
388                                  storage_device_info_,
389                                  file_path,
390                                  base::Unretained(file_info)),
391                       base::Bind(&MTPDeviceDelegateImplWin::OnGetFileInfo,
392                                  weak_ptr_factory_.GetWeakPtr(),
393                                  success_callback,
394                                  error_callback,
395                                  base::Owned(file_info))));
396 }
397
398 void MTPDeviceDelegateImplWin::ReadDirectory(
399     const base::FilePath& root,
400     const ReadDirectorySuccessCallback& success_callback,
401     const ErrorCallback& error_callback) {
402   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
403   DCHECK(!root.empty());
404   fileapi::AsyncFileUtil::EntryList* entries =
405       new fileapi::AsyncFileUtil::EntryList;
406   EnsureInitAndRunTask(
407       PendingTaskInfo(FROM_HERE,
408                       base::Bind(&ReadDirectoryOnBlockingPoolThread,
409                                  storage_device_info_,
410                                  root,
411                                  base::Unretained(entries)),
412                       base::Bind(&MTPDeviceDelegateImplWin::OnDidReadDirectory,
413                                  weak_ptr_factory_.GetWeakPtr(),
414                                  success_callback,
415                                  error_callback,
416                                  base::Owned(entries))));
417 }
418
419 void MTPDeviceDelegateImplWin::CreateSnapshotFile(
420     const base::FilePath& device_file_path,
421     const base::FilePath& snapshot_file_path,
422     const CreateSnapshotFileSuccessCallback& success_callback,
423     const ErrorCallback& error_callback) {
424   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
425   DCHECK(!device_file_path.empty());
426   DCHECK(!snapshot_file_path.empty());
427   scoped_ptr<SnapshotFileDetails> file_details(
428       new SnapshotFileDetails(SnapshotRequestInfo(device_file_path,
429                                                   snapshot_file_path,
430                                                   success_callback,
431                                                   error_callback)));
432   // Passing a raw SnapshotFileDetails* to the blocking pool is safe, because
433   // it is owned by |file_details| in the reply callback.
434   EnsureInitAndRunTask(
435       PendingTaskInfo(FROM_HERE,
436                       base::Bind(&GetFileStreamOnBlockingPoolThread,
437                                  storage_device_info_,
438                                  file_details.get()),
439                       base::Bind(&MTPDeviceDelegateImplWin::OnGetFileStream,
440                                  weak_ptr_factory_.GetWeakPtr(),
441                                  base::Passed(&file_details))));
442 }
443
444 bool MTPDeviceDelegateImplWin::IsStreaming() {
445   return false;
446 }
447
448 void MTPDeviceDelegateImplWin::ReadBytes(
449     const base::FilePath& device_file_path,
450     net::IOBuffer* buf, int64 offset, int buf_len,
451     const ReadBytesSuccessCallback& success_callback,
452     const ErrorCallback& error_callback) {
453   NOTREACHED();
454 }
455
456 void MTPDeviceDelegateImplWin::CancelPendingTasksAndDeleteDelegate() {
457   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
458   PortableDeviceMapService::GetInstance()->MarkPortableDeviceForDeletion(
459       storage_device_info_.registered_device_path);
460   media_task_runner_->PostTask(
461       FROM_HERE,
462       base::Bind(&DeletePortableDeviceOnBlockingPoolThread,
463                  storage_device_info_.registered_device_path));
464   while (!pending_tasks_.empty())
465     pending_tasks_.pop();
466   delete this;
467 }
468
469 void MTPDeviceDelegateImplWin::EnsureInitAndRunTask(
470     const PendingTaskInfo& task_info) {
471   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
472   if ((init_state_ == INITIALIZED) && !task_in_progress_) {
473     DCHECK(pending_tasks_.empty());
474     DCHECK(!current_snapshot_details_.get());
475     base::PostTaskAndReplyWithResult(media_task_runner_,
476                                      task_info.location,
477                                      task_info.task,
478                                      task_info.reply);
479     task_in_progress_ = true;
480     return;
481   }
482
483   pending_tasks_.push(task_info);
484   if (init_state_ == UNINITIALIZED) {
485     init_state_ = PENDING_INIT;
486     base::PostTaskAndReplyWithResult(
487         media_task_runner_,
488         FROM_HERE,
489         base::Bind(&OpenDeviceOnBlockingPoolThread,
490                    storage_device_info_.pnp_device_id,
491                    storage_device_info_.registered_device_path),
492         base::Bind(&MTPDeviceDelegateImplWin::OnInitCompleted,
493                    weak_ptr_factory_.GetWeakPtr()));
494     task_in_progress_ = true;
495   }
496 }
497
498 void MTPDeviceDelegateImplWin::WriteDataChunkIntoSnapshotFile() {
499   DCHECK(current_snapshot_details_.get());
500   base::PostTaskAndReplyWithResult(
501       media_task_runner_,
502       FROM_HERE,
503       base::Bind(&WriteDataChunkIntoSnapshotFileOnBlockingPoolThread,
504                  *current_snapshot_details_),
505       base::Bind(&MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile,
506                  weak_ptr_factory_.GetWeakPtr(),
507                  current_snapshot_details_->request_info().snapshot_file_path));
508 }
509
510 void MTPDeviceDelegateImplWin::ProcessNextPendingRequest() {
511   DCHECK(!task_in_progress_);
512   if (pending_tasks_.empty())
513     return;
514   const PendingTaskInfo& task_info = pending_tasks_.front();
515   task_in_progress_ = true;
516   base::PostTaskAndReplyWithResult(media_task_runner_,
517                                    task_info.location,
518                                    task_info.task,
519                                    task_info.reply);
520   pending_tasks_.pop();
521 }
522
523 void MTPDeviceDelegateImplWin::OnInitCompleted(bool succeeded) {
524   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
525   init_state_ = succeeded ? INITIALIZED : UNINITIALIZED;
526   task_in_progress_ = false;
527   ProcessNextPendingRequest();
528 }
529
530 void MTPDeviceDelegateImplWin::OnGetFileInfo(
531     const GetFileInfoSuccessCallback& success_callback,
532     const ErrorCallback& error_callback,
533     base::File::Info* file_info,
534     base::File::Error error) {
535   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
536   DCHECK(file_info);
537   if (error == base::File::FILE_OK)
538     success_callback.Run(*file_info);
539   else
540     error_callback.Run(error);
541   task_in_progress_ = false;
542   ProcessNextPendingRequest();
543 }
544
545 void MTPDeviceDelegateImplWin::OnDidReadDirectory(
546     const ReadDirectorySuccessCallback& success_callback,
547     const ErrorCallback& error_callback,
548     fileapi::AsyncFileUtil::EntryList* file_list,
549     base::File::Error error) {
550   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
551   DCHECK(file_list);
552   if (error == base::File::FILE_OK)
553     success_callback.Run(*file_list, false /*no more entries*/);
554   else
555     error_callback.Run(error);
556   task_in_progress_ = false;
557   ProcessNextPendingRequest();
558 }
559
560 void MTPDeviceDelegateImplWin::OnGetFileStream(
561     scoped_ptr<SnapshotFileDetails> file_details,
562     base::File::Error error) {
563   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
564   DCHECK(file_details);
565   DCHECK(!file_details->request_info().device_file_path.empty());
566   DCHECK(!file_details->request_info().snapshot_file_path.empty());
567   DCHECK(!current_snapshot_details_.get());
568   if (error != base::File::FILE_OK) {
569     file_details->request_info().error_callback.Run(error);
570     task_in_progress_ = false;
571     ProcessNextPendingRequest();
572     return;
573   }
574   DCHECK(file_details->file_info().size == 0 ||
575          file_details->device_file_stream());
576   current_snapshot_details_.reset(file_details.release());
577   WriteDataChunkIntoSnapshotFile();
578 }
579
580 void MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile(
581     const base::FilePath& snapshot_file_path,
582     DWORD bytes_written) {
583   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
584   DCHECK(!snapshot_file_path.empty());
585   if (!current_snapshot_details_.get())
586     return;
587   DCHECK_EQ(
588       current_snapshot_details_->request_info().snapshot_file_path.value(),
589       snapshot_file_path.value());
590
591   bool succeeded = false;
592   bool should_continue = false;
593   if (current_snapshot_details_->file_info().size > 0) {
594     if (current_snapshot_details_->AddBytesWritten(bytes_written)) {
595       if (current_snapshot_details_->IsSnapshotFileWriteComplete()) {
596         succeeded = true;
597       } else {
598         should_continue = true;
599       }
600     }
601   } else {
602     // Handle empty files.
603     DCHECK_EQ(0U, bytes_written);
604     succeeded = true;
605   }
606
607   if (should_continue) {
608     WriteDataChunkIntoSnapshotFile();
609     return;
610   }
611   if (succeeded) {
612     current_snapshot_details_->request_info().success_callback.Run(
613         current_snapshot_details_->file_info(),
614         current_snapshot_details_->request_info().snapshot_file_path);
615   } else {
616     current_snapshot_details_->request_info().error_callback.Run(
617         base::File::FILE_ERROR_FAILED);
618   }
619   task_in_progress_ = false;
620   current_snapshot_details_.reset();
621   ProcessNextPendingRequest();
622 }