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