Home | History | Annotate | Download | only in win
      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 "storage/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_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);
     55 }
     56 
     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.
     62 //
     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();
     75 
     76   if (device_info.registered_device_path == file_path.value())
     77     return device_info.storage_object_id;
     78 
     79   base::FilePath relative_path;
     80   if (!base::FilePath(device_info.registered_device_path).AppendRelativePath(
     81           file_path, &relative_path))
     82     return base::string16();
     83 
     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;
     96   }
     97   return file_object_id;
     98 }
     99 
    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>();
    114 
    115   base::string16 object_id =
    116       GetFileObjectIdFromPathOnBlockingPoolThread(device_info, root);
    117   if (object_id.empty())
    118     return scoped_ptr<MTPDeviceObjectEnumerator>();
    119 
    120   MTPDeviceObjectEntries entries;
    121   if (!media_transfer_protocol::GetDirectoryEntries(device, object_id,
    122                                                     &entries) ||
    123       entries.empty())
    124     return scoped_ptr<MTPDeviceObjectEnumerator>();
    125 
    126   return scoped_ptr<MTPDeviceObjectEnumerator>(
    127       new MTPDeviceObjectEnumerator(entries));
    128 }
    129 
    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());
    146   }
    147   return init_succeeded;
    148 }
    149 
    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;
    167 
    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);
    174 }
    175 
    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;
    192 
    193   if (!file_info.is_directory)
    194     return base::File::FILE_ERROR_NOT_A_DIRECTORY;
    195 
    196   base::FilePath current;
    197   scoped_ptr<MTPDeviceObjectEnumerator> file_enum =
    198       CreateFileEnumeratorOnBlockingPoolThread(device_info, root);
    199   if (!file_enum)
    200     return error;
    201 
    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);
    209   }
    210   return error;
    211 }
    212 
    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;
    230 
    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;
    236 
    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;
    245 
    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;
    256   }
    257 
    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.
    262   //
    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();
    266 
    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);
    270   file_details->set_optimal_transfer_size(optimal_transfer_size);
    271   return error;
    272 }
    273 
    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());
    287 }
    288 
    289 void DeletePortableDeviceOnBlockingPoolThread(
    290     const base::string16& registered_device_path) {
    291   base::ThreadRestrictions::AssertIOAllowed();
    292   PortableDeviceMapService::GetInstance()->RemovePortableDevice(
    293       registered_device_path);
    294 }
    295 
    296 }  // namespace
    297 
    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));
    314 }
    315 
    316 void CreateMTPDeviceAsyncDelegate(
    317     const base::string16& device_location,
    318     const CreateMTPDeviceAsyncDelegateCallback& callback) {
    319   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    320   DCHECK(!device_location.empty());
    321   base::string16* pnp_device_id = new base::string16;
    322   base::string16* storage_object_id = new base::string16;
    323   content::BrowserThread::PostTaskAndReplyWithResult<bool>(
    324       content::BrowserThread::UI,
    325       FROM_HERE,
    326       base::Bind(&GetStorageInfoOnUIThread,
    327                  device_location,
    328                  base::Unretained(pnp_device_id),
    329                  base::Unretained(storage_object_id)),
    330       base::Bind(&OnGetStorageInfoCreateDelegate,
    331                  device_location,
    332                  callback,
    333                  base::Owned(pnp_device_id),
    334                  base::Owned(storage_object_id)));
    335 }
    336 
    337 // MTPDeviceDelegateImplWin ---------------------------------------------------
    338 
    339 MTPDeviceDelegateImplWin::StorageDeviceInfo::StorageDeviceInfo(
    340     const base::string16& pnp_device_id,
    341     const base::string16& registered_device_path,
    342     const base::string16& storage_object_id)
    343     : pnp_device_id(pnp_device_id),
    344       registered_device_path(registered_device_path),
    345       storage_object_id(storage_object_id) {
    346   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    347 }
    348 
    349 MTPDeviceDelegateImplWin::PendingTaskInfo::PendingTaskInfo(
    350     const tracked_objects::Location& location,
    351     const base::Callback<base::File::Error(void)>& task,
    352     const base::Callback<void(base::File::Error)>& reply)
    353     : location(location),
    354       task(task),
    355       reply(reply) {
    356 }
    357 
    358 MTPDeviceDelegateImplWin::MTPDeviceDelegateImplWin(
    359     const base::string16& registered_device_path,
    360     const base::string16& pnp_device_id,
    361     const base::string16& storage_object_id)
    362     : storage_device_info_(pnp_device_id, registered_device_path,
    363                            storage_object_id),
    364       init_state_(UNINITIALIZED),
    365       media_task_runner_(MediaFileSystemBackend::MediaTaskRunner()),
    366       task_in_progress_(false),
    367       weak_ptr_factory_(this) {
    368   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    369   DCHECK(!registered_device_path.empty());
    370   DCHECK(!pnp_device_id.empty());
    371   DCHECK(!storage_object_id.empty());
    372   DCHECK(media_task_runner_.get());
    373 }
    374 
    375 MTPDeviceDelegateImplWin::~MTPDeviceDelegateImplWin() {
    376   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    377 }
    378 
    379 void MTPDeviceDelegateImplWin::GetFileInfo(
    380     const base::FilePath& file_path,
    381     const GetFileInfoSuccessCallback& success_callback,
    382     const ErrorCallback& error_callback) {
    383   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    384   DCHECK(!file_path.empty());
    385   base::File::Info* file_info = new base::File::Info;
    386   EnsureInitAndRunTask(
    387       PendingTaskInfo(FROM_HERE,
    388                       base::Bind(&GetFileInfoOnBlockingPoolThread,
    389                                  storage_device_info_,
    390                                  file_path,
    391                                  base::Unretained(file_info)),
    392                       base::Bind(&MTPDeviceDelegateImplWin::OnGetFileInfo,
    393                                  weak_ptr_factory_.GetWeakPtr(),
    394                                  success_callback,
    395                                  error_callback,
    396                                  base::Owned(file_info))));
    397 }
    398 
    399 void MTPDeviceDelegateImplWin::ReadDirectory(
    400     const base::FilePath& root,
    401     const ReadDirectorySuccessCallback& success_callback,
    402     const ErrorCallback& error_callback) {
    403   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    404   DCHECK(!root.empty());
    405   storage::AsyncFileUtil::EntryList* entries =
    406       new storage::AsyncFileUtil::EntryList;
    407   EnsureInitAndRunTask(
    408       PendingTaskInfo(FROM_HERE,
    409                       base::Bind(&ReadDirectoryOnBlockingPoolThread,
    410                                  storage_device_info_,
    411                                  root,
    412                                  base::Unretained(entries)),
    413                       base::Bind(&MTPDeviceDelegateImplWin::OnDidReadDirectory,
    414                                  weak_ptr_factory_.GetWeakPtr(),
    415                                  success_callback,
    416                                  error_callback,
    417                                  base::Owned(entries))));
    418 }
    419 
    420 void MTPDeviceDelegateImplWin::CreateSnapshotFile(
    421     const base::FilePath& device_file_path,
    422     const base::FilePath& snapshot_file_path,
    423     const CreateSnapshotFileSuccessCallback& success_callback,
    424     const ErrorCallback& error_callback) {
    425   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    426   DCHECK(!device_file_path.empty());
    427   DCHECK(!snapshot_file_path.empty());
    428   scoped_ptr<SnapshotFileDetails> file_details(
    429       new SnapshotFileDetails(SnapshotRequestInfo(device_file_path,
    430                                                   snapshot_file_path,
    431                                                   success_callback,
    432                                                   error_callback)));
    433   // Passing a raw SnapshotFileDetails* to the blocking pool is safe, because
    434   // it is owned by |file_details| in the reply callback.
    435   EnsureInitAndRunTask(
    436       PendingTaskInfo(FROM_HERE,
    437                       base::Bind(&GetFileStreamOnBlockingPoolThread,
    438                                  storage_device_info_,
    439                                  file_details.get()),
    440                       base::Bind(&MTPDeviceDelegateImplWin::OnGetFileStream,
    441                                  weak_ptr_factory_.GetWeakPtr(),
    442                                  base::Passed(&file_details))));
    443 }
    444 
    445 bool MTPDeviceDelegateImplWin::IsStreaming() {
    446   return false;
    447 }
    448 
    449 void MTPDeviceDelegateImplWin::ReadBytes(
    450     const base::FilePath& device_file_path,
    451     const scoped_refptr<net::IOBuffer>& buf,
    452     int64 offset,
    453     int buf_len,
    454     const ReadBytesSuccessCallback& success_callback,
    455     const ErrorCallback& error_callback) {
    456   NOTREACHED();
    457 }
    458 
    459 void MTPDeviceDelegateImplWin::CancelPendingTasksAndDeleteDelegate() {
    460   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    461   PortableDeviceMapService::GetInstance()->MarkPortableDeviceForDeletion(
    462       storage_device_info_.registered_device_path);
    463   media_task_runner_->PostTask(
    464       FROM_HERE,
    465       base::Bind(&DeletePortableDeviceOnBlockingPoolThread,
    466                  storage_device_info_.registered_device_path));
    467   while (!pending_tasks_.empty())
    468     pending_tasks_.pop();
    469   delete this;
    470 }
    471 
    472 void MTPDeviceDelegateImplWin::EnsureInitAndRunTask(
    473     const PendingTaskInfo& task_info) {
    474   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    475   if ((init_state_ == INITIALIZED) && !task_in_progress_) {
    476     DCHECK(pending_tasks_.empty());
    477     DCHECK(!current_snapshot_details_.get());
    478     base::PostTaskAndReplyWithResult(media_task_runner_,
    479                                      task_info.location,
    480                                      task_info.task,
    481                                      task_info.reply);
    482     task_in_progress_ = true;
    483     return;
    484   }
    485 
    486   pending_tasks_.push(task_info);
    487   if (init_state_ == UNINITIALIZED) {
    488     init_state_ = PENDING_INIT;
    489     base::PostTaskAndReplyWithResult(
    490         media_task_runner_,
    491         FROM_HERE,
    492         base::Bind(&OpenDeviceOnBlockingPoolThread,
    493                    storage_device_info_.pnp_device_id,
    494                    storage_device_info_.registered_device_path),
    495         base::Bind(&MTPDeviceDelegateImplWin::OnInitCompleted,
    496                    weak_ptr_factory_.GetWeakPtr()));
    497     task_in_progress_ = true;
    498   }
    499 }
    500 
    501 void MTPDeviceDelegateImplWin::WriteDataChunkIntoSnapshotFile() {
    502   DCHECK(current_snapshot_details_.get());
    503   base::PostTaskAndReplyWithResult(
    504       media_task_runner_,
    505       FROM_HERE,
    506       base::Bind(&WriteDataChunkIntoSnapshotFileOnBlockingPoolThread,
    507                  *current_snapshot_details_),
    508       base::Bind(&MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile,
    509                  weak_ptr_factory_.GetWeakPtr(),
    510                  current_snapshot_details_->request_info().snapshot_file_path));
    511 }
    512 
    513 void MTPDeviceDelegateImplWin::ProcessNextPendingRequest() {
    514   DCHECK(!task_in_progress_);
    515   if (pending_tasks_.empty())
    516     return;
    517   const PendingTaskInfo& task_info = pending_tasks_.front();
    518   task_in_progress_ = true;
    519   base::PostTaskAndReplyWithResult(media_task_runner_,
    520                                    task_info.location,
    521                                    task_info.task,
    522                                    task_info.reply);
    523   pending_tasks_.pop();
    524 }
    525 
    526 void MTPDeviceDelegateImplWin::OnInitCompleted(bool succeeded) {
    527   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    528   init_state_ = succeeded ? INITIALIZED : UNINITIALIZED;
    529   task_in_progress_ = false;
    530   ProcessNextPendingRequest();
    531 }
    532 
    533 void MTPDeviceDelegateImplWin::OnGetFileInfo(
    534     const GetFileInfoSuccessCallback& success_callback,
    535     const ErrorCallback& error_callback,
    536     base::File::Info* file_info,
    537     base::File::Error error) {
    538   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    539   DCHECK(file_info);
    540   if (error == base::File::FILE_OK)
    541     success_callback.Run(*file_info);
    542   else
    543     error_callback.Run(error);
    544   task_in_progress_ = false;
    545   ProcessNextPendingRequest();
    546 }
    547 
    548 void MTPDeviceDelegateImplWin::OnDidReadDirectory(
    549     const ReadDirectorySuccessCallback& success_callback,
    550     const ErrorCallback& error_callback,
    551     storage::AsyncFileUtil::EntryList* file_list,
    552     base::File::Error error) {
    553   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    554   DCHECK(file_list);
    555   if (error == base::File::FILE_OK)
    556     success_callback.Run(*file_list, false /*no more entries*/);
    557   else
    558     error_callback.Run(error);
    559   task_in_progress_ = false;
    560   ProcessNextPendingRequest();
    561 }
    562 
    563 void MTPDeviceDelegateImplWin::OnGetFileStream(
    564     scoped_ptr<SnapshotFileDetails> file_details,
    565     base::File::Error error) {
    566   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    567   DCHECK(file_details);
    568   DCHECK(!file_details->request_info().device_file_path.empty());
    569   DCHECK(!file_details->request_info().snapshot_file_path.empty());
    570   DCHECK(!current_snapshot_details_.get());
    571   if (error != base::File::FILE_OK) {
    572     file_details->request_info().error_callback.Run(error);
    573     task_in_progress_ = false;
    574     ProcessNextPendingRequest();
    575     return;
    576   }
    577   DCHECK(file_details->file_info().size == 0 ||
    578          file_details->device_file_stream());
    579   current_snapshot_details_.reset(file_details.release());
    580   WriteDataChunkIntoSnapshotFile();
    581 }
    582 
    583 void MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile(
    584     const base::FilePath& snapshot_file_path,
    585     DWORD bytes_written) {
    586   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
    587   DCHECK(!snapshot_file_path.empty());
    588   if (!current_snapshot_details_.get())
    589     return;
    590   DCHECK_EQ(
    591       current_snapshot_details_->request_info().snapshot_file_path.value(),
    592       snapshot_file_path.value());
    593 
    594   bool succeeded = false;
    595   bool should_continue = false;
    596   if (current_snapshot_details_->file_info().size > 0) {
    597     if (current_snapshot_details_->AddBytesWritten(bytes_written)) {
    598       if (current_snapshot_details_->IsSnapshotFileWriteComplete()) {
    599         succeeded = true;
    600       } else {
    601         should_continue = true;
    602       }
    603     }
    604   } else {
    605     // Handle empty files.
    606     DCHECK_EQ(0U, bytes_written);
    607     succeeded = true;
    608   }
    609 
    610   if (should_continue) {
    611     WriteDataChunkIntoSnapshotFile();
    612     return;
    613   }
    614   if (succeeded) {
    615     current_snapshot_details_->request_info().success_callback.Run(
    616         current_snapshot_details_->file_info(),
    617         current_snapshot_details_->request_info().snapshot_file_path);
    618   } else {
    619     current_snapshot_details_->request_info().error_callback.Run(
    620         base::File::FILE_ERROR_FAILED);
    621   }
    622   task_in_progress_ = false;
    623   current_snapshot_details_.reset();
    624   ProcessNextPendingRequest();
    625 }
    626