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.
5 #include "chrome/browser/media_galleries/linux/mtp_device_task_helper.h"
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"
23 using storage_monitor::StorageMonitor
;
28 // This method is used to handle the results of
29 // MediaTransferProtocolManager::CloseStorage method call.
30 void DoNothing(bool error
) {
33 device::MediaTransferProtocolManager
* GetMediaTransferProtocolManager() {
34 return StorageMonitor::GetInstance()->media_transfer_protocol_manager();
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
;
52 MTPDeviceTaskHelper::MTPDeviceTaskHelper()
53 : weak_ptr_factory_(this) {
54 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
57 MTPDeviceTaskHelper::~MTPDeviceTaskHelper() {
58 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
61 void MTPDeviceTaskHelper::OpenStorage(const std::string
& storage_name
,
63 const OpenStorageCallback
& callback
) {
64 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
65 DCHECK(!storage_name
.empty());
66 if (!device_handle_
.empty()) {
67 content::BrowserThread::PostTask(content::BrowserThread::IO
,
69 base::Bind(callback
, true));
73 const std::string mode
=
74 read_only
? mtpd::kReadOnlyMode
: mtpd::kReadWriteMode
;
75 GetMediaTransferProtocolManager()->OpenStorage(
76 storage_name
, mode
, base::Bind(&MTPDeviceTaskHelper::OnDidOpenStorage
,
77 weak_ptr_factory_
.GetWeakPtr(), callback
));
80 void MTPDeviceTaskHelper::GetFileInfo(
82 const GetFileInfoSuccessCallback
& success_callback
,
83 const ErrorCallback
& error_callback
) {
84 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
85 if (device_handle_
.empty())
86 return HandleDeviceError(error_callback
, base::File::FILE_ERROR_FAILED
);
88 GetMediaTransferProtocolManager()->GetFileInfo(
89 device_handle_
, file_id
,
90 base::Bind(&MTPDeviceTaskHelper::OnGetFileInfo
,
91 weak_ptr_factory_
.GetWeakPtr(),
96 void MTPDeviceTaskHelper::CreateDirectory(
97 const uint32 parent_id
,
98 const std::string
& directory_name
,
99 const CreateDirectorySuccessCallback
& success_callback
,
100 const ErrorCallback
& error_callback
) {
101 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
102 if (device_handle_
.empty())
103 return HandleDeviceError(error_callback
, base::File::FILE_ERROR_FAILED
);
105 GetMediaTransferProtocolManager()->CreateDirectory(
106 device_handle_
, parent_id
, directory_name
,
107 base::Bind(&MTPDeviceTaskHelper::OnCreateDirectory
,
108 weak_ptr_factory_
.GetWeakPtr(), success_callback
,
112 void MTPDeviceTaskHelper::ReadDirectory(
113 const uint32 directory_id
,
114 const size_t max_size
,
115 const ReadDirectorySuccessCallback
& success_callback
,
116 const ErrorCallback
& error_callback
) {
117 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
118 if (device_handle_
.empty())
119 return HandleDeviceError(error_callback
, base::File::FILE_ERROR_FAILED
);
121 GetMediaTransferProtocolManager()->ReadDirectory(
122 device_handle_
, directory_id
, max_size
,
123 base::Bind(&MTPDeviceTaskHelper::OnDidReadDirectory
,
124 weak_ptr_factory_
.GetWeakPtr(), success_callback
,
128 void MTPDeviceTaskHelper::WriteDataIntoSnapshotFile(
129 const SnapshotRequestInfo
& request_info
,
130 const base::File::Info
& snapshot_file_info
) {
131 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
132 if (device_handle_
.empty()) {
133 return HandleDeviceError(request_info
.error_callback
,
134 base::File::FILE_ERROR_FAILED
);
137 if (!read_file_worker_
)
138 read_file_worker_
.reset(new MTPReadFileWorker(device_handle_
));
139 read_file_worker_
->WriteDataIntoSnapshotFile(request_info
,
143 void MTPDeviceTaskHelper::ReadBytes(
144 const MTPDeviceAsyncDelegate::ReadBytesRequest
& request
) {
145 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
146 if (device_handle_
.empty()) {
147 return HandleDeviceError(request
.error_callback
,
148 base::File::FILE_ERROR_FAILED
);
151 GetMediaTransferProtocolManager()->GetFileInfo(
152 device_handle_
, request
.file_id
,
153 base::Bind(&MTPDeviceTaskHelper::OnGetFileInfoToReadBytes
,
154 weak_ptr_factory_
.GetWeakPtr(), request
));
157 void MTPDeviceTaskHelper::RenameObject(
158 const uint32 object_id
,
159 const std::string
& new_name
,
160 const RenameObjectSuccessCallback
& success_callback
,
161 const ErrorCallback
& error_callback
) {
162 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
164 GetMediaTransferProtocolManager()->RenameObject(
165 device_handle_
, object_id
, new_name
,
166 base::Bind(&MTPDeviceTaskHelper::OnRenameObject
,
167 weak_ptr_factory_
.GetWeakPtr(), success_callback
,
171 // TODO(yawano) storage_name is not used, delete it.
172 void MTPDeviceTaskHelper::CopyFileFromLocal(
173 const std::string
& storage_name
,
174 const int source_file_descriptor
,
175 const uint32 parent_id
,
176 const std::string
& file_name
,
177 const CopyFileFromLocalSuccessCallback
& success_callback
,
178 const ErrorCallback
& error_callback
) {
179 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
181 GetMediaTransferProtocolManager()->CopyFileFromLocal(
182 device_handle_
, source_file_descriptor
, parent_id
, file_name
,
183 base::Bind(&MTPDeviceTaskHelper::OnCopyFileFromLocal
,
184 weak_ptr_factory_
.GetWeakPtr(), success_callback
,
188 void MTPDeviceTaskHelper::DeleteObject(
189 const uint32 object_id
,
190 const DeleteObjectSuccessCallback
& success_callback
,
191 const ErrorCallback
& error_callback
) {
192 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
194 GetMediaTransferProtocolManager()->DeleteObject(
195 device_handle_
, object_id
,
196 base::Bind(&MTPDeviceTaskHelper::OnDeleteObject
,
197 weak_ptr_factory_
.GetWeakPtr(), success_callback
,
201 void MTPDeviceTaskHelper::CloseStorage() const {
202 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
203 if (device_handle_
.empty())
205 GetMediaTransferProtocolManager()->CloseStorage(device_handle_
,
206 base::Bind(&DoNothing
));
209 void MTPDeviceTaskHelper::OnDidOpenStorage(
210 const OpenStorageCallback
& completion_callback
,
211 const std::string
& device_handle
,
213 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
214 device_handle_
= device_handle
;
215 content::BrowserThread::PostTask(content::BrowserThread::IO
,
217 base::Bind(completion_callback
, !error
));
220 void MTPDeviceTaskHelper::OnGetFileInfo(
221 const GetFileInfoSuccessCallback
& success_callback
,
222 const ErrorCallback
& error_callback
,
223 const MtpFileEntry
& file_entry
,
225 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
227 return HandleDeviceError(error_callback
,
228 base::File::FILE_ERROR_NOT_FOUND
);
231 content::BrowserThread::PostTask(
232 content::BrowserThread::IO
,
234 base::Bind(success_callback
, FileInfoFromMTPFileEntry(file_entry
)));
237 void MTPDeviceTaskHelper::OnCreateDirectory(
238 const CreateDirectorySuccessCallback
& success_callback
,
239 const ErrorCallback
& error_callback
,
240 const bool error
) const {
241 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
243 content::BrowserThread::PostTask(
244 content::BrowserThread::IO
, FROM_HERE
,
245 base::Bind(error_callback
, base::File::FILE_ERROR_FAILED
));
249 content::BrowserThread::PostTask(content::BrowserThread::IO
, FROM_HERE
,
253 void MTPDeviceTaskHelper::OnDidReadDirectory(
254 const ReadDirectorySuccessCallback
& success_callback
,
255 const ErrorCallback
& error_callback
,
256 const std::vector
<MtpFileEntry
>& file_entries
,
259 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
261 return HandleDeviceError(error_callback
, base::File::FILE_ERROR_FAILED
);
263 storage::AsyncFileUtil::EntryList entries
;
264 base::FilePath current
;
265 MTPDeviceObjectEnumerator
file_enum(file_entries
);
266 while (!(current
= file_enum
.Next()).empty()) {
267 storage::DirectoryEntry entry
;
268 entry
.name
= storage::VirtualPath::BaseName(current
).value();
270 bool ret
= file_enum
.GetEntryId(&file_id
);
272 entry
.name
.push_back(',');
273 entry
.name
+= base::UintToString(file_id
);
274 entry
.is_directory
= file_enum
.IsDirectory();
275 entry
.size
= file_enum
.Size();
276 entry
.last_modified_time
= file_enum
.LastModifiedTime();
277 entries
.push_back(entry
);
279 content::BrowserThread::PostTask(
280 content::BrowserThread::IO
,
282 base::Bind(success_callback
, entries
, has_more
));
285 void MTPDeviceTaskHelper::OnGetFileInfoToReadBytes(
286 const MTPDeviceAsyncDelegate::ReadBytesRequest
& request
,
287 const MtpFileEntry
& file_entry
,
289 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
290 DCHECK(request
.buf
.get());
291 DCHECK_GE(request
.buf_len
, 0);
292 DCHECK_GE(request
.offset
, 0);
294 return HandleDeviceError(request
.error_callback
,
295 base::File::FILE_ERROR_FAILED
);
298 base::File::Info file_info
= FileInfoFromMTPFileEntry(file_entry
);
299 if (file_info
.is_directory
) {
300 return HandleDeviceError(request
.error_callback
,
301 base::File::FILE_ERROR_NOT_A_FILE
);
302 } else if (file_info
.size
< 0 || file_info
.size
> kuint32max
||
303 request
.offset
> file_info
.size
) {
304 return HandleDeviceError(request
.error_callback
,
305 base::File::FILE_ERROR_FAILED
);
306 } else if (request
.offset
== file_info
.size
) {
307 content::BrowserThread::PostTask(content::BrowserThread::IO
,
309 base::Bind(request
.success_callback
,
314 uint32 bytes_to_read
= std::min(
315 base::checked_cast
<uint32
>(request
.buf_len
),
316 base::saturated_cast
<uint32
>(file_info
.size
- request
.offset
));
318 GetMediaTransferProtocolManager()->ReadFileChunk(
321 base::checked_cast
<uint32
>(request
.offset
),
323 base::Bind(&MTPDeviceTaskHelper::OnDidReadBytes
,
324 weak_ptr_factory_
.GetWeakPtr(), request
, file_info
));
327 void MTPDeviceTaskHelper::OnDidReadBytes(
328 const MTPDeviceAsyncDelegate::ReadBytesRequest
& request
,
329 const base::File::Info
& file_info
,
330 const std::string
& data
,
332 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
334 return HandleDeviceError(request
.error_callback
,
335 base::File::FILE_ERROR_FAILED
);
338 CHECK_LE(base::checked_cast
<int>(data
.length()), request
.buf_len
);
339 std::copy(data
.begin(), data
.end(), request
.buf
->data());
341 content::BrowserThread::PostTask(content::BrowserThread::IO
,
343 base::Bind(request
.success_callback
,
344 file_info
, data
.length()));
347 void MTPDeviceTaskHelper::OnRenameObject(
348 const RenameObjectSuccessCallback
& success_callback
,
349 const ErrorCallback
& error_callback
,
350 const bool error
) const {
351 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
353 content::BrowserThread::PostTask(
354 content::BrowserThread::IO
, FROM_HERE
,
355 base::Bind(error_callback
, base::File::FILE_ERROR_FAILED
));
359 content::BrowserThread::PostTask(content::BrowserThread::IO
, FROM_HERE
,
363 void MTPDeviceTaskHelper::OnCopyFileFromLocal(
364 const CopyFileFromLocalSuccessCallback
& success_callback
,
365 const ErrorCallback
& error_callback
,
366 const bool error
) const {
367 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
369 content::BrowserThread::PostTask(
370 content::BrowserThread::IO
, FROM_HERE
,
371 base::Bind(error_callback
, base::File::FILE_ERROR_FAILED
));
375 content::BrowserThread::PostTask(content::BrowserThread::IO
, FROM_HERE
,
379 void MTPDeviceTaskHelper::OnDeleteObject(
380 const DeleteObjectSuccessCallback
& success_callback
,
381 const ErrorCallback
& error_callback
,
382 const bool error
) const {
383 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
385 content::BrowserThread::PostTask(
386 content::BrowserThread::IO
, FROM_HERE
,
387 base::Bind(error_callback
, base::File::FILE_ERROR_FAILED
));
391 content::BrowserThread::PostTask(content::BrowserThread::IO
, FROM_HERE
,
392 base::Bind(success_callback
));
395 void MTPDeviceTaskHelper::HandleDeviceError(
396 const ErrorCallback
& error_callback
,
397 base::File::Error error
) const {
398 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
399 content::BrowserThread::PostTask(content::BrowserThread::IO
,
401 base::Bind(error_callback
, error
));