This source file includes following definitions.
- GetStorageInfoOnUIThread
 
- GetFileObjectIdFromPathOnBlockingPoolThread
 
- CreateFileEnumeratorOnBlockingPoolThread
 
- OpenDeviceOnBlockingPoolThread
 
- GetFileInfoOnBlockingPoolThread
 
- ReadDirectoryOnBlockingPoolThread
 
- GetFileStreamOnBlockingPoolThread
 
- WriteDataChunkIntoSnapshotFileOnBlockingPoolThread
 
- DeletePortableDeviceOnBlockingPoolThread
 
- OnGetStorageInfoCreateDelegate
 
- CreateMTPDeviceAsyncDelegate
 
- storage_object_id
 
- reply
 
- weak_ptr_factory_
 
- GetFileInfo
 
- ReadDirectory
 
- CreateSnapshotFile
 
- IsStreaming
 
- ReadBytes
 
- CancelPendingTasksAndDeleteDelegate
 
- EnsureInitAndRunTask
 
- WriteDataChunkIntoSnapshotFile
 
- ProcessNextPendingRequest
 
- OnInitCompleted
 
- OnGetFileInfo
 
- OnDidReadDirectory
 
- OnGetFileStream
 
- OnWroteDataChunkIntoSnapshotFile
 
#include "chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.h"
#include <portabledevice.h>
#include <vector>
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/sequenced_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task_runner_util.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
#include "chrome/browser/media_galleries/win/mtp_device_object_entry.h"
#include "chrome/browser/media_galleries/win/mtp_device_object_enumerator.h"
#include "chrome/browser/media_galleries/win/mtp_device_operations_util.h"
#include "chrome/browser/media_galleries/win/portable_device_map_service.h"
#include "chrome/browser/media_galleries/win/snapshot_file_details.h"
#include "components/storage_monitor/storage_monitor.h"
#include "content/public/browser/browser_thread.h"
#include "webkit/common/fileapi/file_system_util.h"
namespace {
bool GetStorageInfoOnUIThread(const base::string16& storage_path,
                              base::string16* pnp_device_id,
                              base::string16* storage_object_id) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  DCHECK(!storage_path.empty());
  DCHECK(pnp_device_id);
  DCHECK(storage_object_id);
  base::string16 storage_device_id;
  base::RemoveChars(storage_path, L"\\\\", &storage_device_id);
  DCHECK(!storage_device_id.empty());
  
  storage_monitor::StorageMonitor* monitor =
      storage_monitor::StorageMonitor::GetInstance();
  DCHECK(monitor);
  return monitor->GetMTPStorageInfoFromDeviceId(
      base::UTF16ToUTF8(storage_device_id), pnp_device_id, storage_object_id);
}
base::string16 GetFileObjectIdFromPathOnBlockingPoolThread(
    const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
    const base::FilePath& file_path) {
  base::ThreadRestrictions::AssertIOAllowed();
  DCHECK(!file_path.empty());
  IPortableDevice* device =
      PortableDeviceMapService::GetInstance()->GetPortableDevice(
          device_info.registered_device_path);
  if (!device)
    return base::string16();
  if (device_info.registered_device_path == file_path.value())
    return device_info.storage_object_id;
  base::FilePath relative_path;
  if (!base::FilePath(device_info.registered_device_path).AppendRelativePath(
          file_path, &relative_path))
    return base::string16();
  std::vector<base::string16> path_components;
  relative_path.GetComponents(&path_components);
  DCHECK(!path_components.empty());
  base::string16 parent_id(device_info.storage_object_id);
  base::string16 file_object_id;
  for (size_t i = 0; i < path_components.size(); ++i) {
    file_object_id =
        media_transfer_protocol::GetObjectIdFromName(device, parent_id,
                                                     path_components[i]);
    if (file_object_id.empty())
      break;
    parent_id = file_object_id;
  }
  return file_object_id;
}
scoped_ptr<MTPDeviceObjectEnumerator>
CreateFileEnumeratorOnBlockingPoolThread(
    const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
    const base::FilePath& root) {
  base::ThreadRestrictions::AssertIOAllowed();
  DCHECK(!device_info.registered_device_path.empty());
  DCHECK(!root.empty());
  IPortableDevice* device =
      PortableDeviceMapService::GetInstance()->GetPortableDevice(
          device_info.registered_device_path);
  if (!device)
    return scoped_ptr<MTPDeviceObjectEnumerator>();
  base::string16 object_id =
      GetFileObjectIdFromPathOnBlockingPoolThread(device_info, root);
  if (object_id.empty())
    return scoped_ptr<MTPDeviceObjectEnumerator>();
  MTPDeviceObjectEntries entries;
  if (!media_transfer_protocol::GetDirectoryEntries(device, object_id,
                                                    &entries) ||
      entries.empty())
    return scoped_ptr<MTPDeviceObjectEnumerator>();
  return scoped_ptr<MTPDeviceObjectEnumerator>(
      new MTPDeviceObjectEnumerator(entries));
}
bool OpenDeviceOnBlockingPoolThread(
    const base::string16& pnp_device_id,
    const base::string16& registered_device_path) {
  base::ThreadRestrictions::AssertIOAllowed();
  DCHECK(!pnp_device_id.empty());
  DCHECK(!registered_device_path.empty());
  base::win::ScopedComPtr<IPortableDevice> device =
      media_transfer_protocol::OpenDevice(pnp_device_id);
  bool init_succeeded = device.get() != NULL;
  if (init_succeeded) {
    PortableDeviceMapService::GetInstance()->AddPortableDevice(
        registered_device_path, device.get());
  }
  return init_succeeded;
}
base::File::Error GetFileInfoOnBlockingPoolThread(
    const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
    const base::FilePath& file_path,
    base::File::Info* file_info) {
  base::ThreadRestrictions::AssertIOAllowed();
  DCHECK(!device_info.registered_device_path.empty());
  DCHECK(!file_path.empty());
  DCHECK(file_info);
  IPortableDevice* device =
      PortableDeviceMapService::GetInstance()->GetPortableDevice(
          device_info.registered_device_path);
  if (!device)
    return base::File::FILE_ERROR_FAILED;
  base::string16 object_id =
      GetFileObjectIdFromPathOnBlockingPoolThread(device_info, file_path);
  if (object_id.empty())
    return base::File::FILE_ERROR_FAILED;
  return media_transfer_protocol::GetFileEntryInfo(device, object_id,
                                                   file_info);
}
base::File::Error ReadDirectoryOnBlockingPoolThread(
    const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
    const base::FilePath& root,
    fileapi::AsyncFileUtil::EntryList* entries) {
  base::ThreadRestrictions::AssertIOAllowed();
  DCHECK(!root.empty());
  DCHECK(entries);
  base::File::Info file_info;
  base::File::Error error = GetFileInfoOnBlockingPoolThread(device_info, root,
                                                            &file_info);
  if (error != base::File::FILE_OK)
    return error;
  if (!file_info.is_directory)
    return base::File::FILE_ERROR_NOT_A_DIRECTORY;
  base::FilePath current;
  scoped_ptr<MTPDeviceObjectEnumerator> file_enum =
      CreateFileEnumeratorOnBlockingPoolThread(device_info, root);
  if (!file_enum)
    return error;
  while (!(current = file_enum->Next()).empty()) {
    fileapi::DirectoryEntry entry;
    entry.is_directory = file_enum->IsDirectory();
    entry.name = fileapi::VirtualPath::BaseName(current).value();
    entry.size = file_enum->Size();
    entry.last_modified_time = file_enum->LastModifiedTime();
    entries->push_back(entry);
  }
  return error;
}
base::File::Error GetFileStreamOnBlockingPoolThread(
    const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
    SnapshotFileDetails* file_details) {
  base::ThreadRestrictions::AssertIOAllowed();
  DCHECK(file_details);
  DCHECK(!file_details->request_info().device_file_path.empty());
  DCHECK(!file_details->request_info().snapshot_file_path.empty());
  IPortableDevice* device =
      PortableDeviceMapService::GetInstance()->GetPortableDevice(
          device_info.registered_device_path);
  if (!device)
    return base::File::FILE_ERROR_FAILED;
  base::string16 file_object_id =
      GetFileObjectIdFromPathOnBlockingPoolThread(
          device_info, file_details->request_info().device_file_path);
  if (file_object_id.empty())
    return base::File::FILE_ERROR_FAILED;
  base::File::Info file_info;
  base::File::Error error =
      GetFileInfoOnBlockingPoolThread(
          device_info,
          file_details->request_info().device_file_path,
          &file_info);
  if (error != base::File::FILE_OK)
    return error;
  DWORD optimal_transfer_size = 0;
  base::win::ScopedComPtr<IStream> file_stream;
  if (file_info.size > 0) {
    HRESULT hr = media_transfer_protocol::GetFileStreamForObject(
        device,
        file_object_id,
        file_stream.Receive(),
        &optimal_transfer_size);
    if (hr != S_OK)
      return base::File::FILE_ERROR_FAILED;
  }
  
  
  
  
  
  
  
  file_info.last_modified = base::Time();
  DCHECK(file_info.size == 0 || optimal_transfer_size > 0U);
  file_details->set_file_info(file_info);
  file_details->set_device_file_stream(file_stream);
  file_details->set_optimal_transfer_size(optimal_transfer_size);
  return error;
}
DWORD WriteDataChunkIntoSnapshotFileOnBlockingPoolThread(
    const SnapshotFileDetails& file_details) {
  base::ThreadRestrictions::AssertIOAllowed();
  if (file_details.file_info().size == 0)
    return 0;
  return media_transfer_protocol::CopyDataChunkToLocalFile(
      file_details.device_file_stream(),
      file_details.request_info().snapshot_file_path,
      file_details.optimal_transfer_size());
}
void DeletePortableDeviceOnBlockingPoolThread(
    const base::string16& registered_device_path) {
  base::ThreadRestrictions::AssertIOAllowed();
  PortableDeviceMapService::GetInstance()->RemovePortableDevice(
      registered_device_path);
}
}  
void OnGetStorageInfoCreateDelegate(
    const base::string16& device_location,
    const CreateMTPDeviceAsyncDelegateCallback& callback,
    base::string16* pnp_device_id,
    base::string16* storage_object_id,
    bool succeeded) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  DCHECK(pnp_device_id);
  DCHECK(storage_object_id);
  if (!succeeded)
    return;
  callback.Run(new MTPDeviceDelegateImplWin(device_location,
                                            *pnp_device_id,
                                            *storage_object_id));
}
void CreateMTPDeviceAsyncDelegate(
    const base::string16& device_location,
    const CreateMTPDeviceAsyncDelegateCallback& callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  DCHECK(!device_location.empty());
  base::string16* pnp_device_id = new base::string16;
  base::string16* storage_object_id = new base::string16;
  content::BrowserThread::PostTaskAndReplyWithResult<bool>(
      content::BrowserThread::UI,
      FROM_HERE,
      base::Bind(&GetStorageInfoOnUIThread,
                 device_location,
                 base::Unretained(pnp_device_id),
                 base::Unretained(storage_object_id)),
      base::Bind(&OnGetStorageInfoCreateDelegate,
                 device_location,
                 callback,
                 base::Owned(pnp_device_id),
                 base::Owned(storage_object_id)));
}
MTPDeviceDelegateImplWin::StorageDeviceInfo::StorageDeviceInfo(
    const base::string16& pnp_device_id,
    const base::string16& registered_device_path,
    const base::string16& storage_object_id)
    : pnp_device_id(pnp_device_id),
      registered_device_path(registered_device_path),
      storage_object_id(storage_object_id) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
}
MTPDeviceDelegateImplWin::PendingTaskInfo::PendingTaskInfo(
    const tracked_objects::Location& location,
    const base::Callback<base::File::Error(void)>& task,
    const base::Callback<void(base::File::Error)>& reply)
    : location(location),
      task(task),
      reply(reply) {
}
MTPDeviceDelegateImplWin::MTPDeviceDelegateImplWin(
    const base::string16& registered_device_path,
    const base::string16& pnp_device_id,
    const base::string16& storage_object_id)
    : storage_device_info_(pnp_device_id, registered_device_path,
                           storage_object_id),
      init_state_(UNINITIALIZED),
      media_task_runner_(MediaFileSystemBackend::MediaTaskRunner()),
      task_in_progress_(false),
      weak_ptr_factory_(this) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  DCHECK(!registered_device_path.empty());
  DCHECK(!pnp_device_id.empty());
  DCHECK(!storage_object_id.empty());
  DCHECK(media_task_runner_.get());
}
MTPDeviceDelegateImplWin::~MTPDeviceDelegateImplWin() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
}
void MTPDeviceDelegateImplWin::GetFileInfo(
    const base::FilePath& file_path,
    const GetFileInfoSuccessCallback& success_callback,
    const ErrorCallback& error_callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  DCHECK(!file_path.empty());
  base::File::Info* file_info = new base::File::Info;
  EnsureInitAndRunTask(
      PendingTaskInfo(FROM_HERE,
                      base::Bind(&GetFileInfoOnBlockingPoolThread,
                                 storage_device_info_,
                                 file_path,
                                 base::Unretained(file_info)),
                      base::Bind(&MTPDeviceDelegateImplWin::OnGetFileInfo,
                                 weak_ptr_factory_.GetWeakPtr(),
                                 success_callback,
                                 error_callback,
                                 base::Owned(file_info))));
}
void MTPDeviceDelegateImplWin::ReadDirectory(
    const base::FilePath& root,
    const ReadDirectorySuccessCallback& success_callback,
    const ErrorCallback& error_callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  DCHECK(!root.empty());
  fileapi::AsyncFileUtil::EntryList* entries =
      new fileapi::AsyncFileUtil::EntryList;
  EnsureInitAndRunTask(
      PendingTaskInfo(FROM_HERE,
                      base::Bind(&ReadDirectoryOnBlockingPoolThread,
                                 storage_device_info_,
                                 root,
                                 base::Unretained(entries)),
                      base::Bind(&MTPDeviceDelegateImplWin::OnDidReadDirectory,
                                 weak_ptr_factory_.GetWeakPtr(),
                                 success_callback,
                                 error_callback,
                                 base::Owned(entries))));
}
void MTPDeviceDelegateImplWin::CreateSnapshotFile(
    const base::FilePath& device_file_path,
    const base::FilePath& snapshot_file_path,
    const CreateSnapshotFileSuccessCallback& success_callback,
    const ErrorCallback& error_callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  DCHECK(!device_file_path.empty());
  DCHECK(!snapshot_file_path.empty());
  scoped_ptr<SnapshotFileDetails> file_details(
      new SnapshotFileDetails(SnapshotRequestInfo(device_file_path,
                                                  snapshot_file_path,
                                                  success_callback,
                                                  error_callback)));
  
  
  EnsureInitAndRunTask(
      PendingTaskInfo(FROM_HERE,
                      base::Bind(&GetFileStreamOnBlockingPoolThread,
                                 storage_device_info_,
                                 file_details.get()),
                      base::Bind(&MTPDeviceDelegateImplWin::OnGetFileStream,
                                 weak_ptr_factory_.GetWeakPtr(),
                                 base::Passed(&file_details))));
}
bool MTPDeviceDelegateImplWin::IsStreaming() {
  return false;
}
void MTPDeviceDelegateImplWin::ReadBytes(
    const base::FilePath& device_file_path,
    net::IOBuffer* buf, int64 offset, int buf_len,
    const ReadBytesSuccessCallback& success_callback,
    const ErrorCallback& error_callback) {
  NOTREACHED();
}
void MTPDeviceDelegateImplWin::CancelPendingTasksAndDeleteDelegate() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  PortableDeviceMapService::GetInstance()->MarkPortableDeviceForDeletion(
      storage_device_info_.registered_device_path);
  media_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(&DeletePortableDeviceOnBlockingPoolThread,
                 storage_device_info_.registered_device_path));
  while (!pending_tasks_.empty())
    pending_tasks_.pop();
  delete this;
}
void MTPDeviceDelegateImplWin::EnsureInitAndRunTask(
    const PendingTaskInfo& task_info) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  if ((init_state_ == INITIALIZED) && !task_in_progress_) {
    DCHECK(pending_tasks_.empty());
    DCHECK(!current_snapshot_details_.get());
    base::PostTaskAndReplyWithResult(media_task_runner_,
                                     task_info.location,
                                     task_info.task,
                                     task_info.reply);
    task_in_progress_ = true;
    return;
  }
  pending_tasks_.push(task_info);
  if (init_state_ == UNINITIALIZED) {
    init_state_ = PENDING_INIT;
    base::PostTaskAndReplyWithResult(
        media_task_runner_,
        FROM_HERE,
        base::Bind(&OpenDeviceOnBlockingPoolThread,
                   storage_device_info_.pnp_device_id,
                   storage_device_info_.registered_device_path),
        base::Bind(&MTPDeviceDelegateImplWin::OnInitCompleted,
                   weak_ptr_factory_.GetWeakPtr()));
    task_in_progress_ = true;
  }
}
void MTPDeviceDelegateImplWin::WriteDataChunkIntoSnapshotFile() {
  DCHECK(current_snapshot_details_.get());
  base::PostTaskAndReplyWithResult(
      media_task_runner_,
      FROM_HERE,
      base::Bind(&WriteDataChunkIntoSnapshotFileOnBlockingPoolThread,
                 *current_snapshot_details_),
      base::Bind(&MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile,
                 weak_ptr_factory_.GetWeakPtr(),
                 current_snapshot_details_->request_info().snapshot_file_path));
}
void MTPDeviceDelegateImplWin::ProcessNextPendingRequest() {
  DCHECK(!task_in_progress_);
  if (pending_tasks_.empty())
    return;
  const PendingTaskInfo& task_info = pending_tasks_.front();
  task_in_progress_ = true;
  base::PostTaskAndReplyWithResult(media_task_runner_,
                                   task_info.location,
                                   task_info.task,
                                   task_info.reply);
  pending_tasks_.pop();
}
void MTPDeviceDelegateImplWin::OnInitCompleted(bool succeeded) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  init_state_ = succeeded ? INITIALIZED : UNINITIALIZED;
  task_in_progress_ = false;
  ProcessNextPendingRequest();
}
void MTPDeviceDelegateImplWin::OnGetFileInfo(
    const GetFileInfoSuccessCallback& success_callback,
    const ErrorCallback& error_callback,
    base::File::Info* file_info,
    base::File::Error error) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  DCHECK(file_info);
  if (error == base::File::FILE_OK)
    success_callback.Run(*file_info);
  else
    error_callback.Run(error);
  task_in_progress_ = false;
  ProcessNextPendingRequest();
}
void MTPDeviceDelegateImplWin::OnDidReadDirectory(
    const ReadDirectorySuccessCallback& success_callback,
    const ErrorCallback& error_callback,
    fileapi::AsyncFileUtil::EntryList* file_list,
    base::File::Error error) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  DCHECK(file_list);
  if (error == base::File::FILE_OK)
    success_callback.Run(*file_list, false );
  else
    error_callback.Run(error);
  task_in_progress_ = false;
  ProcessNextPendingRequest();
}
void MTPDeviceDelegateImplWin::OnGetFileStream(
    scoped_ptr<SnapshotFileDetails> file_details,
    base::File::Error error) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  DCHECK(file_details);
  DCHECK(!file_details->request_info().device_file_path.empty());
  DCHECK(!file_details->request_info().snapshot_file_path.empty());
  DCHECK(!current_snapshot_details_.get());
  if (error != base::File::FILE_OK) {
    file_details->request_info().error_callback.Run(error);
    task_in_progress_ = false;
    ProcessNextPendingRequest();
    return;
  }
  DCHECK(file_details->file_info().size == 0 ||
         file_details->device_file_stream());
  current_snapshot_details_.reset(file_details.release());
  WriteDataChunkIntoSnapshotFile();
}
void MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile(
    const base::FilePath& snapshot_file_path,
    DWORD bytes_written) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  DCHECK(!snapshot_file_path.empty());
  if (!current_snapshot_details_.get())
    return;
  DCHECK_EQ(
      current_snapshot_details_->request_info().snapshot_file_path.value(),
      snapshot_file_path.value());
  bool succeeded = false;
  bool should_continue = false;
  if (current_snapshot_details_->file_info().size > 0) {
    if (current_snapshot_details_->AddBytesWritten(bytes_written)) {
      if (current_snapshot_details_->IsSnapshotFileWriteComplete()) {
        succeeded = true;
      } else {
        should_continue = true;
      }
    }
  } else {
    
    DCHECK_EQ(0U, bytes_written);
    succeeded = true;
  }
  if (should_continue) {
    WriteDataChunkIntoSnapshotFile();
    return;
  }
  if (succeeded) {
    current_snapshot_details_->request_info().success_callback.Run(
        current_snapshot_details_->file_info(),
        current_snapshot_details_->request_info().snapshot_file_path);
  } else {
    current_snapshot_details_->request_info().error_callback.Run(
        base::File::FILE_ERROR_FAILED);
  }
  task_in_progress_ = false;
  current_snapshot_details_.reset();
  ProcessNextPendingRequest();
}