Home | History | Annotate | Download | only in linux
      1 // Copyright 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 #include "chrome/browser/media_galleries/linux/mtp_device_task_helper.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/logging.h"
     10 #include "base/numerics/safe_conversions.h"
     11 #include "base/strings/string_number_conversions.h"
     12 #include "chrome/browser/media_galleries/linux/mtp_device_object_enumerator.h"
     13 #include "chrome/browser/media_galleries/linux/mtp_read_file_worker.h"
     14 #include "chrome/browser/media_galleries/linux/snapshot_file_details.h"
     15 #include "components/storage_monitor/storage_monitor.h"
     16 #include "content/public/browser/browser_thread.h"
     17 #include "device/media_transfer_protocol/media_transfer_protocol_manager.h"
     18 #include "net/base/io_buffer.h"
     19 #include "storage/browser/fileapi/async_file_util.h"
     20 #include "storage/common/fileapi/file_system_util.h"
     21 #include "third_party/cros_system_api/dbus/service_constants.h"
     22 
     23 using storage_monitor::StorageMonitor;
     24 
     25 namespace {
     26 
     27 // Does nothing.
     28 // This method is used to handle the results of
     29 // MediaTransferProtocolManager::CloseStorage method call.
     30 void DoNothing(bool error) {
     31 }
     32 
     33 device::MediaTransferProtocolManager* GetMediaTransferProtocolManager() {
     34   return StorageMonitor::GetInstance()->media_transfer_protocol_manager();
     35 }
     36 
     37 base::File::Info FileInfoFromMTPFileEntry(const MtpFileEntry& file_entry) {
     38   base::File::Info file_entry_info;
     39   file_entry_info.size = file_entry.file_size();
     40   file_entry_info.is_directory =
     41       file_entry.file_type() == MtpFileEntry::FILE_TYPE_FOLDER;
     42   file_entry_info.is_symbolic_link = false;
     43   file_entry_info.last_modified =
     44       base::Time::FromTimeT(file_entry.modification_time());
     45   file_entry_info.last_accessed = file_entry_info.last_modified;
     46   file_entry_info.creation_time = base::Time();
     47   return file_entry_info;
     48 }
     49 
     50 }  // namespace
     51 
     52 MTPDeviceTaskHelper::MTPDeviceTaskHelper()
     53     : weak_ptr_factory_(this) {
     54   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
     55 }
     56 
     57 MTPDeviceTaskHelper::~MTPDeviceTaskHelper() {
     58   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
     59 }
     60 
     61 void MTPDeviceTaskHelper::OpenStorage(const std::string& storage_name,
     62                                       const OpenStorageCallback& callback) {
     63   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
     64   DCHECK(!storage_name.empty());
     65   if (!device_handle_.empty()) {
     66     content::BrowserThread::PostTask(content::BrowserThread::IO,
     67                                      FROM_HERE,
     68                                      base::Bind(callback, true));
     69     return;
     70   }
     71   GetMediaTransferProtocolManager()->OpenStorage(
     72       storage_name, mtpd::kReadOnlyMode,
     73       base::Bind(&MTPDeviceTaskHelper::OnDidOpenStorage,
     74                  weak_ptr_factory_.GetWeakPtr(),
     75                  callback));
     76 }
     77 
     78 void MTPDeviceTaskHelper::GetFileInfo(
     79     uint32 file_id,
     80     const GetFileInfoSuccessCallback& success_callback,
     81     const ErrorCallback& error_callback) {
     82   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
     83   if (device_handle_.empty())
     84     return HandleDeviceError(error_callback, base::File::FILE_ERROR_FAILED);
     85 
     86   GetMediaTransferProtocolManager()->GetFileInfo(
     87       device_handle_, file_id,
     88       base::Bind(&MTPDeviceTaskHelper::OnGetFileInfo,
     89                  weak_ptr_factory_.GetWeakPtr(),
     90                  success_callback,
     91                  error_callback));
     92 }
     93 
     94 void MTPDeviceTaskHelper::ReadDirectory(
     95     uint32 dir_id,
     96     const ReadDirectorySuccessCallback& success_callback,
     97     const ErrorCallback& error_callback) {
     98   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
     99   if (device_handle_.empty())
    100     return HandleDeviceError(error_callback, base::File::FILE_ERROR_FAILED);
    101 
    102   GetMediaTransferProtocolManager()->ReadDirectory(
    103       device_handle_, dir_id,
    104       base::Bind(&MTPDeviceTaskHelper::OnDidReadDirectory,
    105                  weak_ptr_factory_.GetWeakPtr(),
    106                  success_callback,
    107                  error_callback));
    108 }
    109 
    110 void MTPDeviceTaskHelper::WriteDataIntoSnapshotFile(
    111     const SnapshotRequestInfo& request_info,
    112     const base::File::Info& snapshot_file_info) {
    113   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    114   if (device_handle_.empty()) {
    115     return HandleDeviceError(request_info.error_callback,
    116                              base::File::FILE_ERROR_FAILED);
    117   }
    118 
    119   if (!read_file_worker_)
    120     read_file_worker_.reset(new MTPReadFileWorker(device_handle_));
    121   read_file_worker_->WriteDataIntoSnapshotFile(request_info,
    122                                                snapshot_file_info);
    123 }
    124 
    125 void MTPDeviceTaskHelper::ReadBytes(
    126     const MTPDeviceAsyncDelegate::ReadBytesRequest& request) {
    127   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    128   if (device_handle_.empty()) {
    129     return HandleDeviceError(request.error_callback,
    130                              base::File::FILE_ERROR_FAILED);
    131   }
    132 
    133   GetMediaTransferProtocolManager()->GetFileInfo(
    134       device_handle_, request.file_id,
    135       base::Bind(&MTPDeviceTaskHelper::OnGetFileInfoToReadBytes,
    136                  weak_ptr_factory_.GetWeakPtr(), request));
    137 }
    138 
    139 void MTPDeviceTaskHelper::CloseStorage() const {
    140   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    141   if (device_handle_.empty())
    142     return;
    143   GetMediaTransferProtocolManager()->CloseStorage(device_handle_,
    144                                                   base::Bind(&DoNothing));
    145 }
    146 
    147 void MTPDeviceTaskHelper::OnDidOpenStorage(
    148     const OpenStorageCallback& completion_callback,
    149     const std::string& device_handle,
    150     bool error) {
    151   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    152   device_handle_ = device_handle;
    153   content::BrowserThread::PostTask(content::BrowserThread::IO,
    154                                    FROM_HERE,
    155                                    base::Bind(completion_callback, !error));
    156 }
    157 
    158 void MTPDeviceTaskHelper::OnGetFileInfo(
    159     const GetFileInfoSuccessCallback& success_callback,
    160     const ErrorCallback& error_callback,
    161     const MtpFileEntry& file_entry,
    162     bool error) const {
    163   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    164   if (error) {
    165     return HandleDeviceError(error_callback,
    166                              base::File::FILE_ERROR_NOT_FOUND);
    167   }
    168 
    169   content::BrowserThread::PostTask(
    170       content::BrowserThread::IO,
    171       FROM_HERE,
    172       base::Bind(success_callback, FileInfoFromMTPFileEntry(file_entry)));
    173 }
    174 
    175 void MTPDeviceTaskHelper::OnDidReadDirectory(
    176     const ReadDirectorySuccessCallback& success_callback,
    177     const ErrorCallback& error_callback,
    178     const std::vector<MtpFileEntry>& file_entries,
    179     bool has_more,
    180     bool error) const {
    181   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    182   if (error)
    183     return HandleDeviceError(error_callback, base::File::FILE_ERROR_FAILED);
    184 
    185   storage::AsyncFileUtil::EntryList entries;
    186   base::FilePath current;
    187   MTPDeviceObjectEnumerator file_enum(file_entries);
    188   while (!(current = file_enum.Next()).empty()) {
    189     storage::DirectoryEntry entry;
    190     entry.name = storage::VirtualPath::BaseName(current).value();
    191     uint32 file_id = 0;
    192     bool ret = file_enum.GetEntryId(&file_id);
    193     DCHECK(ret);
    194     entry.name.push_back(',');
    195     entry.name += base::UintToString(file_id);
    196     entry.is_directory = file_enum.IsDirectory();
    197     entry.size = file_enum.Size();
    198     entry.last_modified_time = file_enum.LastModifiedTime();
    199     entries.push_back(entry);
    200   }
    201   content::BrowserThread::PostTask(
    202       content::BrowserThread::IO,
    203       FROM_HERE,
    204       base::Bind(success_callback, entries, has_more));
    205 }
    206 
    207 void MTPDeviceTaskHelper::OnGetFileInfoToReadBytes(
    208     const MTPDeviceAsyncDelegate::ReadBytesRequest& request,
    209     const MtpFileEntry& file_entry,
    210     bool error) {
    211   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    212   DCHECK(request.buf.get());
    213   DCHECK_GE(request.buf_len, 0);
    214   DCHECK_GE(request.offset, 0);
    215   if (error) {
    216     return HandleDeviceError(request.error_callback,
    217                              base::File::FILE_ERROR_FAILED);
    218   }
    219 
    220   base::File::Info file_info = FileInfoFromMTPFileEntry(file_entry);
    221   if (file_info.is_directory) {
    222     return HandleDeviceError(request.error_callback,
    223                              base::File::FILE_ERROR_NOT_A_FILE);
    224   } else if (file_info.size < 0 || file_info.size > kuint32max ||
    225              request.offset > file_info.size) {
    226     return HandleDeviceError(request.error_callback,
    227                              base::File::FILE_ERROR_FAILED);
    228   } else if (request.offset == file_info.size) {
    229     content::BrowserThread::PostTask(content::BrowserThread::IO,
    230                                      FROM_HERE,
    231                                      base::Bind(request.success_callback,
    232                                                 file_info, 0u));
    233     return;
    234   }
    235 
    236   uint32 bytes_to_read = std::min(
    237       base::checked_cast<uint32>(request.buf_len),
    238       base::saturated_cast<uint32>(file_info.size - request.offset));
    239 
    240   GetMediaTransferProtocolManager()->ReadFileChunk(
    241       device_handle_,
    242       request.file_id,
    243       base::checked_cast<uint32>(request.offset),
    244       bytes_to_read,
    245       base::Bind(&MTPDeviceTaskHelper::OnDidReadBytes,
    246                  weak_ptr_factory_.GetWeakPtr(), request, file_info));
    247 }
    248 
    249 void MTPDeviceTaskHelper::OnDidReadBytes(
    250     const MTPDeviceAsyncDelegate::ReadBytesRequest& request,
    251     const base::File::Info& file_info,
    252     const std::string& data,
    253     bool error) const {
    254   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    255   if (error) {
    256     return HandleDeviceError(request.error_callback,
    257                              base::File::FILE_ERROR_FAILED);
    258   }
    259 
    260   CHECK_LE(base::checked_cast<int>(data.length()), request.buf_len);
    261   std::copy(data.begin(), data.end(), request.buf->data());
    262 
    263   content::BrowserThread::PostTask(content::BrowserThread::IO,
    264                                    FROM_HERE,
    265                                    base::Bind(request.success_callback,
    266                                               file_info, data.length()));
    267 }
    268 
    269 void MTPDeviceTaskHelper::HandleDeviceError(
    270     const ErrorCallback& error_callback,
    271     base::File::Error error) const {
    272   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    273   content::BrowserThread::PostTask(content::BrowserThread::IO,
    274                                    FROM_HERE,
    275                                    base::Bind(error_callback, error));
    276 }
    277