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
,
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
,
68 base::Bind(callback
, true));
71 GetMediaTransferProtocolManager()->OpenStorage(
72 storage_name
, mtpd::kReadOnlyMode
,
73 base::Bind(&MTPDeviceTaskHelper::OnDidOpenStorage
,
74 weak_ptr_factory_
.GetWeakPtr(),
78 void MTPDeviceTaskHelper::GetFileInfo(
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
);
86 GetMediaTransferProtocolManager()->GetFileInfo(
87 device_handle_
, file_id
,
88 base::Bind(&MTPDeviceTaskHelper::OnGetFileInfo
,
89 weak_ptr_factory_
.GetWeakPtr(),
94 void MTPDeviceTaskHelper::ReadDirectory(
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
);
102 GetMediaTransferProtocolManager()->ReadDirectory(
103 device_handle_
, dir_id
,
104 base::Bind(&MTPDeviceTaskHelper::OnDidReadDirectory
,
105 weak_ptr_factory_
.GetWeakPtr(),
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
);
119 if (!read_file_worker_
)
120 read_file_worker_
.reset(new MTPReadFileWorker(device_handle_
));
121 read_file_worker_
->WriteDataIntoSnapshotFile(request_info
,
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
);
133 GetMediaTransferProtocolManager()->GetFileInfo(
134 device_handle_
, request
.file_id
,
135 base::Bind(&MTPDeviceTaskHelper::OnGetFileInfoToReadBytes
,
136 weak_ptr_factory_
.GetWeakPtr(), request
));
139 void MTPDeviceTaskHelper::CloseStorage() const {
140 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
141 if (device_handle_
.empty())
143 GetMediaTransferProtocolManager()->CloseStorage(device_handle_
,
144 base::Bind(&DoNothing
));
147 void MTPDeviceTaskHelper::OnDidOpenStorage(
148 const OpenStorageCallback
& completion_callback
,
149 const std::string
& device_handle
,
151 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
152 device_handle_
= device_handle
;
153 content::BrowserThread::PostTask(content::BrowserThread::IO
,
155 base::Bind(completion_callback
, !error
));
158 void MTPDeviceTaskHelper::OnGetFileInfo(
159 const GetFileInfoSuccessCallback
& success_callback
,
160 const ErrorCallback
& error_callback
,
161 const MtpFileEntry
& file_entry
,
163 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
165 return HandleDeviceError(error_callback
,
166 base::File::FILE_ERROR_NOT_FOUND
);
169 content::BrowserThread::PostTask(
170 content::BrowserThread::IO
,
172 base::Bind(success_callback
, FileInfoFromMTPFileEntry(file_entry
)));
175 void MTPDeviceTaskHelper::OnDidReadDirectory(
176 const ReadDirectorySuccessCallback
& success_callback
,
177 const ErrorCallback
& error_callback
,
178 const std::vector
<MtpFileEntry
>& file_entries
,
181 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
183 return HandleDeviceError(error_callback
, base::File::FILE_ERROR_FAILED
);
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();
192 bool ret
= file_enum
.GetEntryId(&file_id
);
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
);
201 content::BrowserThread::PostTask(
202 content::BrowserThread::IO
,
204 base::Bind(success_callback
, entries
, has_more
));
207 void MTPDeviceTaskHelper::OnGetFileInfoToReadBytes(
208 const MTPDeviceAsyncDelegate::ReadBytesRequest
& request
,
209 const MtpFileEntry
& file_entry
,
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);
216 return HandleDeviceError(request
.error_callback
,
217 base::File::FILE_ERROR_FAILED
);
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
,
231 base::Bind(request
.success_callback
,
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
));
240 GetMediaTransferProtocolManager()->ReadFileChunk(
243 base::checked_cast
<uint32
>(request
.offset
),
245 base::Bind(&MTPDeviceTaskHelper::OnDidReadBytes
,
246 weak_ptr_factory_
.GetWeakPtr(), request
, file_info
));
249 void MTPDeviceTaskHelper::OnDidReadBytes(
250 const MTPDeviceAsyncDelegate::ReadBytesRequest
& request
,
251 const base::File::Info
& file_info
,
252 const std::string
& data
,
254 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
256 return HandleDeviceError(request
.error_callback
,
257 base::File::FILE_ERROR_FAILED
);
260 CHECK_LE(base::checked_cast
<int>(data
.length()), request
.buf_len
);
261 std::copy(data
.begin(), data
.end(), request
.buf
->data());
263 content::BrowserThread::PostTask(content::BrowserThread::IO
,
265 base::Bind(request
.success_callback
,
266 file_info
, data
.length()));
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
,
275 base::Bind(error_callback
, error
));