NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / media_galleries / linux / mtp_device_delegate_impl_linux.cc
blob50f6291400d111d00acb186aba83813b121fb75e
1 // Copyright (c) 2012 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_delegate_impl_linux.h"
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "base/strings/string_util.h"
10 #include "chrome/browser/media_galleries/linux/mtp_device_task_helper.h"
11 #include "chrome/browser/media_galleries/linux/mtp_device_task_helper_map_service.h"
12 #include "chrome/browser/media_galleries/linux/snapshot_file_details.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "net/base/io_buffer.h"
16 namespace {
18 // File path separator constant.
19 const char kRootPath[] = "/";
21 // Returns the device relative file path given |file_path|.
22 // E.g.: If the |file_path| is "/usb:2,2:12345/DCIM" and |registered_dev_path|
23 // is "/usb:2,2:12345", this function returns the device relative path which is
24 // "/DCIM".
25 std::string GetDeviceRelativePath(const base::FilePath& registered_dev_path,
26 const base::FilePath& file_path) {
27 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
28 DCHECK(!registered_dev_path.empty());
29 DCHECK(!file_path.empty());
30 if (registered_dev_path == file_path)
31 return kRootPath;
33 base::FilePath relative_path;
34 if (!registered_dev_path.AppendRelativePath(file_path, &relative_path))
35 return std::string();
36 DCHECK(!relative_path.empty());
37 return relative_path.value();
40 // Returns the MTPDeviceTaskHelper object associated with the MTP device
41 // storage.
43 // |storage_name| specifies the name of the storage device.
44 // Returns NULL if the |storage_name| is no longer valid (e.g. because the
45 // corresponding storage device is detached, etc).
46 MTPDeviceTaskHelper* GetDeviceTaskHelperForStorage(
47 const std::string& storage_name) {
48 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
49 return MTPDeviceTaskHelperMapService::GetInstance()->GetDeviceTaskHelper(
50 storage_name);
53 // Opens the storage device for communication.
55 // Called on the UI thread to dispatch the request to the
56 // MediaTransferProtocolManager.
58 // |storage_name| specifies the name of the storage device.
59 // |reply_callback| is called when the OpenStorage request completes.
60 // |reply_callback| runs on the IO thread.
61 void OpenStorageOnUIThread(
62 const std::string& storage_name,
63 const base::Callback<void(bool)>& reply_callback) {
64 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
65 MTPDeviceTaskHelper* task_helper =
66 GetDeviceTaskHelperForStorage(storage_name);
67 if (!task_helper) {
68 task_helper =
69 MTPDeviceTaskHelperMapService::GetInstance()->CreateDeviceTaskHelper(
70 storage_name);
72 task_helper->OpenStorage(storage_name, reply_callback);
75 // Enumerates the |root| directory file entries.
77 // Called on the UI thread to dispatch the request to the
78 // MediaTransferProtocolManager.
80 // |storage_name| specifies the name of the storage device.
81 // |success_callback| is called when the ReadDirectory request succeeds.
82 // |error_callback| is called when the ReadDirectory request fails.
83 // |success_callback| and |error_callback| runs on the IO thread.
84 void ReadDirectoryOnUIThread(
85 const std::string& storage_name,
86 const std::string& root,
87 const base::Callback<
88 void(const fileapi::AsyncFileUtil::EntryList&)>& success_callback,
89 const base::Callback<void(base::File::Error)>& error_callback) {
90 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
91 MTPDeviceTaskHelper* task_helper =
92 GetDeviceTaskHelperForStorage(storage_name);
93 if (!task_helper)
94 return;
95 task_helper->ReadDirectoryByPath(root, success_callback, error_callback);
98 // Gets the |file_path| details.
100 // Called on the UI thread to dispatch the request to the
101 // MediaTransferProtocolManager.
103 // |storage_name| specifies the name of the storage device.
104 // |success_callback| is called when the GetFileInfo request succeeds.
105 // |error_callback| is called when the GetFileInfo request fails.
106 // |success_callback| and |error_callback| runs on the IO thread.
107 void GetFileInfoOnUIThread(
108 const std::string& storage_name,
109 const std::string& file_path,
110 const base::Callback<void(const base::File::Info&)>& success_callback,
111 const base::Callback<void(base::File::Error)>& error_callback) {
112 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
113 MTPDeviceTaskHelper* task_helper =
114 GetDeviceTaskHelperForStorage(storage_name);
115 if (!task_helper)
116 return;
117 task_helper->GetFileInfoByPath(file_path, success_callback, error_callback);
120 // Copies the contents of |device_file_path| to |snapshot_file_path|.
122 // Called on the UI thread to dispatch the request to the
123 // MediaTransferProtocolManager.
125 // |storage_name| specifies the name of the storage device.
126 // |device_file_path| specifies the media device file path.
127 // |snapshot_file_path| specifies the platform path of the snapshot file.
128 // |file_size| specifies the number of bytes that will be written to the
129 // snapshot file.
130 // |success_callback| is called when the copy operation succeeds.
131 // |error_callback| is called when the copy operation fails.
132 // |success_callback| and |error_callback| runs on the IO thread.
133 void WriteDataIntoSnapshotFileOnUIThread(
134 const std::string& storage_name,
135 const SnapshotRequestInfo& request_info,
136 const base::File::Info& snapshot_file_info) {
137 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
138 MTPDeviceTaskHelper* task_helper =
139 GetDeviceTaskHelperForStorage(storage_name);
140 if (!task_helper)
141 return;
142 task_helper->WriteDataIntoSnapshotFile(request_info, snapshot_file_info);
145 // Copies the contents of |device_file_path| to |snapshot_file_path|.
147 // Called on the UI thread to dispatch the request to the
148 // MediaTransferProtocolManager.
150 // |storage_name| specifies the name of the storage device.
151 // |request| is a struct containing details about the byte read request.
152 void ReadBytesOnUIThread(
153 const std::string& storage_name,
154 const MTPDeviceAsyncDelegate::ReadBytesRequest& request) {
155 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
156 MTPDeviceTaskHelper* task_helper =
157 GetDeviceTaskHelperForStorage(storage_name);
158 if (!task_helper)
159 return;
160 task_helper->ReadBytes(request);
163 // Closes the device storage specified by the |storage_name| and destroys the
164 // MTPDeviceTaskHelper object associated with the device storage.
166 // Called on the UI thread to dispatch the request to the
167 // MediaTransferProtocolManager.
168 void CloseStorageAndDestroyTaskHelperOnUIThread(
169 const std::string& storage_name) {
170 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
171 MTPDeviceTaskHelper* task_helper =
172 GetDeviceTaskHelperForStorage(storage_name);
173 if (!task_helper)
174 return;
175 task_helper->CloseStorage();
176 MTPDeviceTaskHelperMapService::GetInstance()->DestroyDeviceTaskHelper(
177 storage_name);
180 } // namespace
182 MTPDeviceDelegateImplLinux::PendingTaskInfo::PendingTaskInfo(
183 const tracked_objects::Location& location,
184 const base::Closure& task)
185 : location(location),
186 task(task) {
189 MTPDeviceDelegateImplLinux::PendingTaskInfo::~PendingTaskInfo() {
192 MTPDeviceDelegateImplLinux::MTPDeviceDelegateImplLinux(
193 const std::string& device_location)
194 : init_state_(UNINITIALIZED),
195 task_in_progress_(false),
196 device_path_(device_location),
197 weak_ptr_factory_(this) {
198 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
199 DCHECK(!device_path_.empty());
200 base::RemoveChars(device_location, kRootPath, &storage_name_);
201 DCHECK(!storage_name_.empty());
204 MTPDeviceDelegateImplLinux::~MTPDeviceDelegateImplLinux() {
205 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
208 void MTPDeviceDelegateImplLinux::GetFileInfo(
209 const base::FilePath& file_path,
210 const GetFileInfoSuccessCallback& success_callback,
211 const ErrorCallback& error_callback) {
212 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
213 DCHECK(!file_path.empty());
214 base::Closure call_closure =
215 base::Bind(&GetFileInfoOnUIThread,
216 storage_name_,
217 GetDeviceRelativePath(device_path_, file_path),
218 base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfo,
219 weak_ptr_factory_.GetWeakPtr(),
220 success_callback),
221 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
222 weak_ptr_factory_.GetWeakPtr(),
223 error_callback));
224 EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE, call_closure));
227 void MTPDeviceDelegateImplLinux::ReadDirectory(
228 const base::FilePath& root,
229 const ReadDirectorySuccessCallback& success_callback,
230 const ErrorCallback& error_callback) {
231 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
232 DCHECK(!root.empty());
233 std::string device_file_relative_path = GetDeviceRelativePath(device_path_,
234 root);
235 base::Closure call_closure =
236 base::Bind(
237 &GetFileInfoOnUIThread,
238 storage_name_,
239 device_file_relative_path,
240 base::Bind(
241 &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory,
242 weak_ptr_factory_.GetWeakPtr(),
243 device_file_relative_path,
244 success_callback,
245 error_callback),
246 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
247 weak_ptr_factory_.GetWeakPtr(),
248 error_callback));
249 EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE, call_closure));
252 void MTPDeviceDelegateImplLinux::CreateSnapshotFile(
253 const base::FilePath& device_file_path,
254 const base::FilePath& snapshot_file_path,
255 const CreateSnapshotFileSuccessCallback& success_callback,
256 const ErrorCallback& error_callback) {
257 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
258 DCHECK(!device_file_path.empty());
259 DCHECK(!snapshot_file_path.empty());
260 std::string device_file_relative_path =
261 GetDeviceRelativePath(device_path_, device_file_path);
262 scoped_ptr<SnapshotRequestInfo> request_info(
263 new SnapshotRequestInfo(device_file_relative_path,
264 snapshot_file_path,
265 success_callback,
266 error_callback));
267 base::Closure call_closure =
268 base::Bind(
269 &GetFileInfoOnUIThread,
270 storage_name_,
271 device_file_relative_path,
272 base::Bind(
273 &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile,
274 weak_ptr_factory_.GetWeakPtr(),
275 base::Passed(&request_info)),
276 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
277 weak_ptr_factory_.GetWeakPtr(),
278 error_callback));
279 EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE, call_closure));
282 bool MTPDeviceDelegateImplLinux::IsStreaming() {
283 return false;
286 void MTPDeviceDelegateImplLinux::ReadBytes(
287 const base::FilePath& device_file_path,
288 net::IOBuffer* buf, int64 offset, int buf_len,
289 const ReadBytesSuccessCallback& success_callback,
290 const ErrorCallback& error_callback) {
291 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
292 DCHECK(!device_file_path.empty());
293 std::string device_file_relative_path =
294 GetDeviceRelativePath(device_path_, device_file_path);
295 ReadBytesRequest request(device_file_relative_path, buf, offset, buf_len,
296 success_callback, error_callback);
297 base::Closure call_closure =
298 base::Bind(
299 &GetFileInfoOnUIThread,
300 storage_name_,
301 device_file_relative_path,
302 base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadBytes,
303 weak_ptr_factory_.GetWeakPtr(), request),
304 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
305 weak_ptr_factory_.GetWeakPtr(), error_callback));
306 EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE, call_closure));
309 void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() {
310 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
311 // To cancel all the pending tasks, destroy the MTPDeviceTaskHelper object.
312 content::BrowserThread::PostTask(
313 content::BrowserThread::UI,
314 FROM_HERE,
315 base::Bind(&CloseStorageAndDestroyTaskHelperOnUIThread, storage_name_));
316 delete this;
319 void MTPDeviceDelegateImplLinux::EnsureInitAndRunTask(
320 const PendingTaskInfo& task_info) {
321 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
322 if ((init_state_ == INITIALIZED) && !task_in_progress_) {
323 task_in_progress_ = true;
324 content::BrowserThread::PostTask(content::BrowserThread::UI,
325 task_info.location,
326 task_info.task);
327 return;
329 pending_tasks_.push(task_info);
330 if (init_state_ == UNINITIALIZED) {
331 init_state_ = PENDING_INIT;
332 task_in_progress_ = true;
333 content::BrowserThread::PostTask(
334 content::BrowserThread::UI,
335 FROM_HERE,
336 base::Bind(&OpenStorageOnUIThread,
337 storage_name_,
338 base::Bind(&MTPDeviceDelegateImplLinux::OnInitCompleted,
339 weak_ptr_factory_.GetWeakPtr())));
343 void MTPDeviceDelegateImplLinux::WriteDataIntoSnapshotFile(
344 const base::File::Info& file_info) {
345 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
346 DCHECK(current_snapshot_request_info_.get());
347 DCHECK_GT(file_info.size, 0);
348 task_in_progress_ = true;
349 SnapshotRequestInfo request_info(
350 current_snapshot_request_info_->device_file_path,
351 current_snapshot_request_info_->snapshot_file_path,
352 base::Bind(
353 &MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile,
354 weak_ptr_factory_.GetWeakPtr()),
355 base::Bind(
356 &MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError,
357 weak_ptr_factory_.GetWeakPtr()));
359 base::Closure task_closure = base::Bind(&WriteDataIntoSnapshotFileOnUIThread,
360 storage_name_,
361 request_info,
362 file_info);
363 content::BrowserThread::PostTask(content::BrowserThread::UI,
364 FROM_HERE,
365 task_closure);
368 void MTPDeviceDelegateImplLinux::ProcessNextPendingRequest() {
369 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
370 DCHECK(!task_in_progress_);
371 if (pending_tasks_.empty())
372 return;
374 task_in_progress_ = true;
375 const PendingTaskInfo& task_info = pending_tasks_.front();
376 content::BrowserThread::PostTask(content::BrowserThread::UI,
377 task_info.location,
378 task_info.task);
379 pending_tasks_.pop();
382 void MTPDeviceDelegateImplLinux::OnInitCompleted(bool succeeded) {
383 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
384 init_state_ = succeeded ? INITIALIZED : UNINITIALIZED;
385 task_in_progress_ = false;
386 ProcessNextPendingRequest();
389 void MTPDeviceDelegateImplLinux::OnDidGetFileInfo(
390 const GetFileInfoSuccessCallback& success_callback,
391 const base::File::Info& file_info) {
392 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
393 success_callback.Run(file_info);
394 task_in_progress_ = false;
395 ProcessNextPendingRequest();
398 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory(
399 const std::string& root,
400 const ReadDirectorySuccessCallback& success_callback,
401 const ErrorCallback& error_callback,
402 const base::File::Info& file_info) {
403 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
404 DCHECK(task_in_progress_);
405 if (!file_info.is_directory) {
406 return HandleDeviceFileError(error_callback,
407 base::File::FILE_ERROR_NOT_A_DIRECTORY);
410 base::Closure task_closure =
411 base::Bind(&ReadDirectoryOnUIThread,
412 storage_name_,
413 root,
414 base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadDirectory,
415 weak_ptr_factory_.GetWeakPtr(),
416 success_callback),
417 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
418 weak_ptr_factory_.GetWeakPtr(),
419 error_callback));
420 content::BrowserThread::PostTask(content::BrowserThread::UI,
421 FROM_HERE,
422 task_closure);
425 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile(
426 scoped_ptr<SnapshotRequestInfo> snapshot_request_info,
427 const base::File::Info& file_info) {
428 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
429 DCHECK(!current_snapshot_request_info_.get());
430 DCHECK(snapshot_request_info.get());
431 DCHECK(task_in_progress_);
432 base::File::Error error = base::File::FILE_OK;
433 if (file_info.is_directory)
434 error = base::File::FILE_ERROR_NOT_A_FILE;
435 else if (file_info.size < 0 || file_info.size > kuint32max)
436 error = base::File::FILE_ERROR_FAILED;
438 if (error != base::File::FILE_OK)
439 return HandleDeviceFileError(snapshot_request_info->error_callback, error);
441 base::File::Info snapshot_file_info(file_info);
442 // Modify the last modified time to null. This prevents the time stamp
443 // verfication in LocalFileStreamReader.
444 snapshot_file_info.last_modified = base::Time();
446 current_snapshot_request_info_.reset(snapshot_request_info.release());
447 if (file_info.size == 0) {
448 // Empty snapshot file.
449 return OnDidWriteDataIntoSnapshotFile(
450 snapshot_file_info, current_snapshot_request_info_->snapshot_file_path);
452 WriteDataIntoSnapshotFile(snapshot_file_info);
455 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadBytes(
456 const ReadBytesRequest& request,
457 const base::File::Info& file_info) {
458 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
459 DCHECK(request.buf);
460 DCHECK(request.buf_len >= 0);
461 DCHECK(task_in_progress_);
462 base::File::Error error = base::File::FILE_OK;
463 if (file_info.is_directory)
464 error = base::File::FILE_ERROR_NOT_A_FILE;
465 else if (file_info.size < 0 || file_info.size > kuint32max)
466 error = base::File::FILE_ERROR_FAILED;
468 if (error != base::File::FILE_OK)
469 return HandleDeviceFileError(request.error_callback, error);
471 ReadBytesRequest new_request(
472 request.device_file_relative_path,
473 request.buf,
474 request.offset,
475 request.buf_len,
476 base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadBytes,
477 weak_ptr_factory_.GetWeakPtr(), request.success_callback),
478 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
479 weak_ptr_factory_.GetWeakPtr(), request.error_callback));
481 content::BrowserThread::PostTask(
482 content::BrowserThread::UI,
483 FROM_HERE,
484 base::Bind(&ReadBytesOnUIThread, storage_name_, new_request));
487 void MTPDeviceDelegateImplLinux::OnDidReadDirectory(
488 const ReadDirectorySuccessCallback& success_callback,
489 const fileapi::AsyncFileUtil::EntryList& file_list) {
490 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
491 success_callback.Run(file_list, false /*no more entries*/);
492 task_in_progress_ = false;
493 ProcessNextPendingRequest();
496 void MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile(
497 const base::File::Info& file_info,
498 const base::FilePath& snapshot_file_path) {
499 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
500 DCHECK(current_snapshot_request_info_.get());
501 DCHECK(task_in_progress_);
502 current_snapshot_request_info_->success_callback.Run(
503 file_info, snapshot_file_path);
504 task_in_progress_ = false;
505 current_snapshot_request_info_.reset();
506 ProcessNextPendingRequest();
509 void MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError(
510 base::File::Error error) {
511 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
512 DCHECK(current_snapshot_request_info_.get());
513 DCHECK(task_in_progress_);
514 current_snapshot_request_info_->error_callback.Run(error);
515 task_in_progress_ = false;
516 current_snapshot_request_info_.reset();
517 ProcessNextPendingRequest();
520 void MTPDeviceDelegateImplLinux::OnDidReadBytes(
521 const ReadBytesSuccessCallback& success_callback,
522 int bytes_read) {
523 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
524 DCHECK(task_in_progress_);
525 success_callback.Run(bytes_read);
526 task_in_progress_ = false;
527 ProcessNextPendingRequest();
530 void MTPDeviceDelegateImplLinux::HandleDeviceFileError(
531 const ErrorCallback& error_callback,
532 base::File::Error error) {
533 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
534 DCHECK(task_in_progress_);
535 error_callback.Run(error);
536 task_in_progress_ = false;
537 ProcessNextPendingRequest();
540 void CreateMTPDeviceAsyncDelegate(
541 const std::string& device_location,
542 const CreateMTPDeviceAsyncDelegateCallback& callback) {
543 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
544 callback.Run(new MTPDeviceDelegateImplLinux(device_location));