Disable accessible touch exploration by default.
[chromium-blink-merge.git] / chrome / browser / media_galleries / linux / mtp_device_delegate_impl_linux.cc
blobf816c9e2cc57821a5b7a590d6ac6b3b8dbeb4baf
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 <algorithm>
8 #include <set>
9 #include <vector>
11 #include "base/bind.h"
12 #include "base/numerics/safe_conversions.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "chrome/browser/media_galleries/linux/mtp_device_task_helper.h"
17 #include "chrome/browser/media_galleries/linux/mtp_device_task_helper_map_service.h"
18 #include "chrome/browser/media_galleries/linux/snapshot_file_details.h"
19 #include "net/base/io_buffer.h"
20 #include "third_party/cros_system_api/dbus/service_constants.h"
22 namespace {
24 // File path separator constant.
25 const char kRootPath[] = "/";
27 // Returns the device relative file path given |file_path|.
28 // E.g.: If the |file_path| is "/usb:2,2:12345/DCIM" and |registered_dev_path|
29 // is "/usb:2,2:12345", this function returns the device relative path which is
30 // "DCIM".
31 // In the special case when |registered_dev_path| and |file_path| are the same,
32 // return |kRootPath|.
33 std::string GetDeviceRelativePath(const base::FilePath& registered_dev_path,
34 const base::FilePath& file_path) {
35 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
36 DCHECK(!registered_dev_path.empty());
37 DCHECK(!file_path.empty());
38 std::string result;
39 if (registered_dev_path == file_path) {
40 result = kRootPath;
41 } else {
42 base::FilePath relative_path;
43 if (registered_dev_path.AppendRelativePath(file_path, &relative_path)) {
44 DCHECK(!relative_path.empty());
45 result = relative_path.value();
48 return result;
51 // Returns the MTPDeviceTaskHelper object associated with the MTP device
52 // storage.
54 // |storage_name| specifies the name of the storage device.
55 // Returns NULL if the |storage_name| is no longer valid (e.g. because the
56 // corresponding storage device is detached, etc).
57 MTPDeviceTaskHelper* GetDeviceTaskHelperForStorage(
58 const std::string& storage_name) {
59 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
60 return MTPDeviceTaskHelperMapService::GetInstance()->GetDeviceTaskHelper(
61 storage_name);
64 // Opens the storage device for communication.
66 // Called on the UI thread to dispatch the request to the
67 // MediaTransferProtocolManager.
69 // |storage_name| specifies the name of the storage device.
70 // |reply_callback| is called when the OpenStorage request completes.
71 // |reply_callback| runs on the IO thread.
72 void OpenStorageOnUIThread(
73 const std::string& storage_name,
74 const base::Callback<void(bool)>& reply_callback) {
75 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
76 MTPDeviceTaskHelper* task_helper =
77 GetDeviceTaskHelperForStorage(storage_name);
78 if (!task_helper) {
79 task_helper =
80 MTPDeviceTaskHelperMapService::GetInstance()->CreateDeviceTaskHelper(
81 storage_name);
83 task_helper->OpenStorage(storage_name, reply_callback);
86 // Enumerates the |dir_id| directory file entries.
88 // Called on the UI thread to dispatch the request to the
89 // MediaTransferProtocolManager.
91 // |storage_name| specifies the name of the storage device.
92 // |success_callback| is called when the ReadDirectory request succeeds.
93 // |error_callback| is called when the ReadDirectory request fails.
94 // |success_callback| and |error_callback| runs on the IO thread.
95 void ReadDirectoryOnUIThread(
96 const std::string& storage_name,
97 uint32 dir_id,
98 const base::Callback<
99 void(const fileapi::AsyncFileUtil::EntryList&)>& success_callback,
100 const base::Callback<void(base::File::Error)>& error_callback) {
101 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
102 MTPDeviceTaskHelper* task_helper =
103 GetDeviceTaskHelperForStorage(storage_name);
104 if (!task_helper)
105 return;
106 task_helper->ReadDirectoryById(dir_id, success_callback, error_callback);
109 // Gets the |file_path| details.
111 // Called on the UI thread to dispatch the request to the
112 // MediaTransferProtocolManager.
114 // |storage_name| specifies the name of the storage device.
115 // |success_callback| is called when the GetFileInfo request succeeds.
116 // |error_callback| is called when the GetFileInfo request fails.
117 // |success_callback| and |error_callback| runs on the IO thread.
118 void GetFileInfoOnUIThread(
119 const std::string& storage_name,
120 uint32 file_id,
121 const MTPDeviceAsyncDelegate::GetFileInfoSuccessCallback& success_callback,
122 const MTPDeviceAsyncDelegate::ErrorCallback& error_callback) {
123 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
124 MTPDeviceTaskHelper* task_helper =
125 GetDeviceTaskHelperForStorage(storage_name);
126 if (!task_helper)
127 return;
128 task_helper->GetFileInfoById(file_id, success_callback, error_callback);
131 // Copies the contents of |device_file_path| to |snapshot_file_path|.
133 // Called on the UI thread to dispatch the request to the
134 // MediaTransferProtocolManager.
136 // |storage_name| specifies the name of the storage device.
137 // |device_file_path| specifies the media device file path.
138 // |snapshot_file_path| specifies the platform path of the snapshot file.
139 // |file_size| specifies the number of bytes that will be written to the
140 // snapshot file.
141 // |success_callback| is called when the copy operation succeeds.
142 // |error_callback| is called when the copy operation fails.
143 // |success_callback| and |error_callback| runs on the IO thread.
144 void WriteDataIntoSnapshotFileOnUIThread(
145 const std::string& storage_name,
146 const SnapshotRequestInfo& request_info,
147 const base::File::Info& snapshot_file_info) {
148 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
149 MTPDeviceTaskHelper* task_helper =
150 GetDeviceTaskHelperForStorage(storage_name);
151 if (!task_helper)
152 return;
153 task_helper->WriteDataIntoSnapshotFile(request_info, snapshot_file_info);
156 // Copies the contents of |device_file_path| to |snapshot_file_path|.
158 // Called on the UI thread to dispatch the request to the
159 // MediaTransferProtocolManager.
161 // |storage_name| specifies the name of the storage device.
162 // |request| is a struct containing details about the byte read request.
163 void ReadBytesOnUIThread(
164 const std::string& storage_name,
165 const MTPDeviceAsyncDelegate::ReadBytesRequest& request) {
166 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
167 MTPDeviceTaskHelper* task_helper =
168 GetDeviceTaskHelperForStorage(storage_name);
169 if (!task_helper)
170 return;
171 task_helper->ReadBytes(request);
174 // Closes the device storage specified by the |storage_name| and destroys the
175 // MTPDeviceTaskHelper object associated with the device storage.
177 // Called on the UI thread to dispatch the request to the
178 // MediaTransferProtocolManager.
179 void CloseStorageAndDestroyTaskHelperOnUIThread(
180 const std::string& storage_name) {
181 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
182 MTPDeviceTaskHelper* task_helper =
183 GetDeviceTaskHelperForStorage(storage_name);
184 if (!task_helper)
185 return;
186 task_helper->CloseStorage();
187 MTPDeviceTaskHelperMapService::GetInstance()->DestroyDeviceTaskHelper(
188 storage_name);
191 } // namespace
193 MTPDeviceDelegateImplLinux::PendingTaskInfo::PendingTaskInfo(
194 const base::FilePath& path,
195 content::BrowserThread::ID thread_id,
196 const tracked_objects::Location& location,
197 const base::Closure& task)
198 : path(path),
199 thread_id(thread_id),
200 location(location),
201 task(task) {
204 MTPDeviceDelegateImplLinux::PendingTaskInfo::~PendingTaskInfo() {
207 // Represents a file on the MTP device.
208 // Lives on the IO thread.
209 class MTPDeviceDelegateImplLinux::MTPFileNode {
210 public:
211 MTPFileNode(uint32 file_id,
212 MTPFileNode* parent,
213 FileIdToMTPFileNodeMap* file_id_to_node_map);
214 ~MTPFileNode();
216 const MTPFileNode* GetChild(const std::string& name) const;
218 void EnsureChildExists(const std::string& name, uint32 id);
220 // Clears all the children, except those in |children_to_keep|.
221 void ClearNonexistentChildren(
222 const std::set<std::string>& children_to_keep);
224 bool DeleteChild(uint32 file_id);
226 uint32 file_id() const { return file_id_; }
227 MTPFileNode* parent() { return parent_; }
229 private:
230 // Container for holding a node's children.
231 typedef base::ScopedPtrHashMap<std::string, MTPFileNode> ChildNodes;
233 const uint32 file_id_;
234 ChildNodes children_;
235 MTPFileNode* const parent_;
236 FileIdToMTPFileNodeMap* file_id_to_node_map_;
238 DISALLOW_COPY_AND_ASSIGN(MTPFileNode);
241 MTPDeviceDelegateImplLinux::MTPFileNode::MTPFileNode(
242 uint32 file_id,
243 MTPFileNode* parent,
244 FileIdToMTPFileNodeMap* file_id_to_node_map)
245 : file_id_(file_id),
246 parent_(parent),
247 file_id_to_node_map_(file_id_to_node_map) {
248 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
249 DCHECK(file_id_to_node_map_);
250 DCHECK(!ContainsKey(*file_id_to_node_map_, file_id_));
251 (*file_id_to_node_map_)[file_id_] = this;
254 MTPDeviceDelegateImplLinux::MTPFileNode::~MTPFileNode() {
255 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
256 size_t erased = file_id_to_node_map_->erase(file_id_);
257 DCHECK_EQ(1U, erased);
260 const MTPDeviceDelegateImplLinux::MTPFileNode*
261 MTPDeviceDelegateImplLinux::MTPFileNode::GetChild(
262 const std::string& name) const {
263 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
264 return children_.get(name);
267 void MTPDeviceDelegateImplLinux::MTPFileNode::EnsureChildExists(
268 const std::string& name,
269 uint32 id) {
270 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
271 const MTPFileNode* child = GetChild(name);
272 if (child && child->file_id() == id)
273 return;
275 children_.set(
276 name,
277 make_scoped_ptr(new MTPFileNode(id, this, file_id_to_node_map_)));
280 void MTPDeviceDelegateImplLinux::MTPFileNode::ClearNonexistentChildren(
281 const std::set<std::string>& children_to_keep) {
282 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
283 std::set<std::string> children_to_erase;
284 for (ChildNodes::const_iterator it = children_.begin();
285 it != children_.end(); ++it) {
286 if (ContainsKey(children_to_keep, it->first))
287 continue;
288 children_to_erase.insert(it->first);
290 for (std::set<std::string>::iterator it = children_to_erase.begin();
291 it != children_to_erase.end(); ++it) {
292 children_.take_and_erase(*it);
296 bool MTPDeviceDelegateImplLinux::MTPFileNode::DeleteChild(uint32 file_id) {
297 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
298 for (ChildNodes::iterator it = children_.begin();
299 it != children_.end(); ++it) {
300 if (it->second->file_id() == file_id) {
301 children_.erase(it);
302 return true;
305 return false;
308 MTPDeviceDelegateImplLinux::MTPDeviceDelegateImplLinux(
309 const std::string& device_location)
310 : init_state_(UNINITIALIZED),
311 task_in_progress_(false),
312 device_path_(device_location),
313 root_node_(new MTPFileNode(mtpd::kRootFileId,
314 NULL,
315 &file_id_to_node_map_)),
316 weak_ptr_factory_(this) {
317 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
318 DCHECK(!device_path_.empty());
319 base::RemoveChars(device_location, kRootPath, &storage_name_);
320 DCHECK(!storage_name_.empty());
323 MTPDeviceDelegateImplLinux::~MTPDeviceDelegateImplLinux() {
324 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
327 void MTPDeviceDelegateImplLinux::GetFileInfo(
328 const base::FilePath& file_path,
329 const GetFileInfoSuccessCallback& success_callback,
330 const ErrorCallback& error_callback) {
331 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
332 DCHECK(!file_path.empty());
333 base::Closure closure =
334 base::Bind(&MTPDeviceDelegateImplLinux::GetFileInfoInternal,
335 weak_ptr_factory_.GetWeakPtr(),
336 file_path,
337 success_callback,
338 error_callback);
339 EnsureInitAndRunTask(PendingTaskInfo(file_path,
340 content::BrowserThread::IO,
341 FROM_HERE,
342 closure));
345 void MTPDeviceDelegateImplLinux::ReadDirectory(
346 const base::FilePath& root,
347 const ReadDirectorySuccessCallback& success_callback,
348 const ErrorCallback& error_callback) {
349 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
350 DCHECK(!root.empty());
351 base::Closure closure =
352 base::Bind(&MTPDeviceDelegateImplLinux::ReadDirectoryInternal,
353 weak_ptr_factory_.GetWeakPtr(),
354 root,
355 success_callback,
356 error_callback);
357 EnsureInitAndRunTask(PendingTaskInfo(root,
358 content::BrowserThread::IO,
359 FROM_HERE,
360 closure));
363 void MTPDeviceDelegateImplLinux::CreateSnapshotFile(
364 const base::FilePath& device_file_path,
365 const base::FilePath& local_path,
366 const CreateSnapshotFileSuccessCallback& success_callback,
367 const ErrorCallback& error_callback) {
368 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
369 DCHECK(!device_file_path.empty());
370 DCHECK(!local_path.empty());
371 base::Closure closure =
372 base::Bind(&MTPDeviceDelegateImplLinux::CreateSnapshotFileInternal,
373 weak_ptr_factory_.GetWeakPtr(),
374 device_file_path,
375 local_path,
376 success_callback,
377 error_callback);
378 EnsureInitAndRunTask(PendingTaskInfo(device_file_path,
379 content::BrowserThread::IO,
380 FROM_HERE,
381 closure));
384 bool MTPDeviceDelegateImplLinux::IsStreaming() {
385 return true;
388 void MTPDeviceDelegateImplLinux::ReadBytes(
389 const base::FilePath& device_file_path,
390 net::IOBuffer* buf, int64 offset, int buf_len,
391 const ReadBytesSuccessCallback& success_callback,
392 const ErrorCallback& error_callback) {
393 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
394 DCHECK(!device_file_path.empty());
395 base::Closure closure =
396 base::Bind(&MTPDeviceDelegateImplLinux::ReadBytesInternal,
397 weak_ptr_factory_.GetWeakPtr(),
398 device_file_path,
399 make_scoped_refptr(buf),
400 offset,
401 buf_len,
402 success_callback,
403 error_callback);
404 EnsureInitAndRunTask(PendingTaskInfo(device_file_path,
405 content::BrowserThread::IO,
406 FROM_HERE,
407 closure));
410 void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() {
411 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
412 // To cancel all the pending tasks, destroy the MTPDeviceTaskHelper object.
413 content::BrowserThread::PostTask(
414 content::BrowserThread::UI,
415 FROM_HERE,
416 base::Bind(&CloseStorageAndDestroyTaskHelperOnUIThread, storage_name_));
417 delete this;
420 void MTPDeviceDelegateImplLinux::GetFileInfoInternal(
421 const base::FilePath& file_path,
422 const GetFileInfoSuccessCallback& success_callback,
423 const ErrorCallback& error_callback) {
424 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
426 uint32 file_id;
427 if (CachedPathToId(file_path, &file_id)) {
428 GetFileInfoSuccessCallback success_callback_wrapper =
429 base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfo,
430 weak_ptr_factory_.GetWeakPtr(),
431 success_callback);
432 ErrorCallback error_callback_wrapper =
433 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
434 weak_ptr_factory_.GetWeakPtr(),
435 error_callback,
436 file_id);
439 base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
440 storage_name_,
441 file_id,
442 success_callback_wrapper,
443 error_callback_wrapper);
444 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
445 content::BrowserThread::UI,
446 FROM_HERE,
447 closure));
448 } else {
449 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
451 PendingRequestDone();
454 void MTPDeviceDelegateImplLinux::ReadDirectoryInternal(
455 const base::FilePath& root,
456 const ReadDirectorySuccessCallback& success_callback,
457 const ErrorCallback& error_callback) {
458 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
460 uint32 dir_id;
461 if (CachedPathToId(root, &dir_id)) {
462 GetFileInfoSuccessCallback success_callback_wrapper =
463 base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory,
464 weak_ptr_factory_.GetWeakPtr(),
465 dir_id,
466 success_callback,
467 error_callback);
468 ErrorCallback error_callback_wrapper =
469 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
470 weak_ptr_factory_.GetWeakPtr(),
471 error_callback,
472 dir_id);
473 base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
474 storage_name_,
475 dir_id,
476 success_callback_wrapper,
477 error_callback_wrapper);
478 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
479 content::BrowserThread::UI,
480 FROM_HERE,
481 closure));
482 } else {
483 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
485 PendingRequestDone();
488 void MTPDeviceDelegateImplLinux::CreateSnapshotFileInternal(
489 const base::FilePath& device_file_path,
490 const base::FilePath& local_path,
491 const CreateSnapshotFileSuccessCallback& success_callback,
492 const ErrorCallback& error_callback) {
493 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
495 uint32 file_id;
496 if (CachedPathToId(device_file_path, &file_id)) {
497 scoped_ptr<SnapshotRequestInfo> request_info(
498 new SnapshotRequestInfo(file_id,
499 local_path,
500 success_callback,
501 error_callback));
502 GetFileInfoSuccessCallback success_callback_wrapper =
503 base::Bind(
504 &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile,
505 weak_ptr_factory_.GetWeakPtr(),
506 base::Passed(&request_info));
507 ErrorCallback error_callback_wrapper =
508 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
509 weak_ptr_factory_.GetWeakPtr(),
510 error_callback,
511 file_id);
512 base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
513 storage_name_,
514 file_id,
515 success_callback_wrapper,
516 error_callback_wrapper);
517 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
518 content::BrowserThread::UI,
519 FROM_HERE,
520 closure));
521 } else {
522 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
524 PendingRequestDone();
527 void MTPDeviceDelegateImplLinux::ReadBytesInternal(
528 const base::FilePath& device_file_path,
529 net::IOBuffer* buf, int64 offset, int buf_len,
530 const ReadBytesSuccessCallback& success_callback,
531 const ErrorCallback& error_callback) {
532 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
534 uint32 file_id;
535 if (CachedPathToId(device_file_path, &file_id)) {
536 ReadBytesRequest request(
537 file_id, buf, offset, buf_len,
538 base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadBytes,
539 weak_ptr_factory_.GetWeakPtr(),
540 success_callback),
541 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
542 weak_ptr_factory_.GetWeakPtr(),
543 error_callback,
544 file_id));
546 base::Closure closure =
547 base::Bind(base::Bind(&ReadBytesOnUIThread, storage_name_, request));
548 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
549 content::BrowserThread::UI,
550 FROM_HERE,
551 closure));
552 } else {
553 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
555 PendingRequestDone();
558 void MTPDeviceDelegateImplLinux::EnsureInitAndRunTask(
559 const PendingTaskInfo& task_info) {
560 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
561 if ((init_state_ == INITIALIZED) && !task_in_progress_) {
562 RunTask(task_info);
563 return;
566 // Only *Internal functions have empty paths. Since they are the continuation
567 // of the current running task, they get to cut in line.
568 if (task_info.path.empty())
569 pending_tasks_.push_front(task_info);
570 else
571 pending_tasks_.push_back(task_info);
573 if (init_state_ == UNINITIALIZED) {
574 init_state_ = PENDING_INIT;
575 task_in_progress_ = true;
576 content::BrowserThread::PostTask(
577 content::BrowserThread::UI,
578 FROM_HERE,
579 base::Bind(&OpenStorageOnUIThread,
580 storage_name_,
581 base::Bind(&MTPDeviceDelegateImplLinux::OnInitCompleted,
582 weak_ptr_factory_.GetWeakPtr())));
586 void MTPDeviceDelegateImplLinux::RunTask(const PendingTaskInfo& task_info) {
587 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
588 DCHECK_EQ(INITIALIZED, init_state_);
589 DCHECK(!task_in_progress_);
590 task_in_progress_ = true;
592 bool need_to_check_cache = !task_info.path.empty();
593 if (need_to_check_cache) {
594 base::FilePath uncached_path =
595 NextUncachedPathComponent(task_info.path, task_info.cached_path);
596 if (!uncached_path.empty()) {
597 // Save the current task and do a cache lookup first.
598 pending_tasks_.push_front(task_info);
599 FillFileCache(uncached_path);
600 return;
604 content::BrowserThread::PostTask(task_info.thread_id,
605 task_info.location,
606 task_info.task);
609 void MTPDeviceDelegateImplLinux::WriteDataIntoSnapshotFile(
610 const base::File::Info& file_info) {
611 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
612 DCHECK(current_snapshot_request_info_.get());
613 DCHECK_GT(file_info.size, 0);
614 DCHECK(task_in_progress_);
615 SnapshotRequestInfo request_info(
616 current_snapshot_request_info_->file_id,
617 current_snapshot_request_info_->snapshot_file_path,
618 base::Bind(
619 &MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile,
620 weak_ptr_factory_.GetWeakPtr()),
621 base::Bind(
622 &MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError,
623 weak_ptr_factory_.GetWeakPtr()));
625 base::Closure task_closure = base::Bind(&WriteDataIntoSnapshotFileOnUIThread,
626 storage_name_,
627 request_info,
628 file_info);
629 content::BrowserThread::PostTask(content::BrowserThread::UI,
630 FROM_HERE,
631 task_closure);
634 void MTPDeviceDelegateImplLinux::PendingRequestDone() {
635 DCHECK(task_in_progress_);
636 task_in_progress_ = false;
637 ProcessNextPendingRequest();
640 void MTPDeviceDelegateImplLinux::ProcessNextPendingRequest() {
641 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
642 DCHECK(!task_in_progress_);
643 if (pending_tasks_.empty())
644 return;
646 PendingTaskInfo task_info = pending_tasks_.front();
647 pending_tasks_.pop_front();
648 RunTask(task_info);
651 void MTPDeviceDelegateImplLinux::OnInitCompleted(bool succeeded) {
652 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
653 init_state_ = succeeded ? INITIALIZED : UNINITIALIZED;
654 PendingRequestDone();
657 void MTPDeviceDelegateImplLinux::OnDidGetFileInfo(
658 const GetFileInfoSuccessCallback& success_callback,
659 const base::File::Info& file_info) {
660 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
661 success_callback.Run(file_info);
662 PendingRequestDone();
665 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory(
666 uint32 dir_id,
667 const ReadDirectorySuccessCallback& success_callback,
668 const ErrorCallback& error_callback,
669 const base::File::Info& file_info) {
670 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
671 DCHECK(task_in_progress_);
672 if (!file_info.is_directory) {
673 return HandleDeviceFileError(error_callback,
674 dir_id,
675 base::File::FILE_ERROR_NOT_A_DIRECTORY);
678 base::Closure task_closure =
679 base::Bind(&ReadDirectoryOnUIThread,
680 storage_name_,
681 dir_id,
682 base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadDirectory,
683 weak_ptr_factory_.GetWeakPtr(),
684 dir_id,
685 success_callback),
686 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
687 weak_ptr_factory_.GetWeakPtr(),
688 error_callback,
689 dir_id));
690 content::BrowserThread::PostTask(content::BrowserThread::UI,
691 FROM_HERE,
692 task_closure);
695 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile(
696 scoped_ptr<SnapshotRequestInfo> snapshot_request_info,
697 const base::File::Info& file_info) {
698 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
699 DCHECK(!current_snapshot_request_info_.get());
700 DCHECK(snapshot_request_info.get());
701 DCHECK(task_in_progress_);
702 base::File::Error error = base::File::FILE_OK;
703 if (file_info.is_directory)
704 error = base::File::FILE_ERROR_NOT_A_FILE;
705 else if (file_info.size < 0 || file_info.size > kuint32max)
706 error = base::File::FILE_ERROR_FAILED;
708 if (error != base::File::FILE_OK)
709 return HandleDeviceFileError(snapshot_request_info->error_callback,
710 snapshot_request_info->file_id,
711 error);
713 base::File::Info snapshot_file_info(file_info);
714 // Modify the last modified time to null. This prevents the time stamp
715 // verfication in LocalFileStreamReader.
716 snapshot_file_info.last_modified = base::Time();
718 current_snapshot_request_info_.reset(snapshot_request_info.release());
719 if (file_info.size == 0) {
720 // Empty snapshot file.
721 return OnDidWriteDataIntoSnapshotFile(
722 snapshot_file_info, current_snapshot_request_info_->snapshot_file_path);
724 WriteDataIntoSnapshotFile(snapshot_file_info);
727 void MTPDeviceDelegateImplLinux::OnDidReadDirectory(
728 uint32 dir_id,
729 const ReadDirectorySuccessCallback& success_callback,
730 const fileapi::AsyncFileUtil::EntryList& file_list) {
731 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
733 FileIdToMTPFileNodeMap::iterator it = file_id_to_node_map_.find(dir_id);
734 DCHECK(it != file_id_to_node_map_.end());
735 MTPFileNode* dir_node = it->second;
736 std::set<std::string> children_to_keep;
738 fileapi::AsyncFileUtil::EntryList normalized_file_list;
739 for (size_t i = 0; i < file_list.size(); ++i) {
740 normalized_file_list.push_back(file_list[i]);
741 fileapi::DirectoryEntry& entry = normalized_file_list.back();
743 // |entry.name| has the file id encoded in it. Decode here.
744 size_t separator_idx = entry.name.find_last_of(',');
745 DCHECK_NE(std::string::npos, separator_idx);
746 std::string file_id_str = entry.name.substr(separator_idx);
747 file_id_str = file_id_str.substr(1); // Get rid of the comma.
748 uint32 file_id = 0;
749 bool ret = base::StringToUint(file_id_str, &file_id);
750 DCHECK(ret);
751 entry.name = entry.name.substr(0, separator_idx);
753 // Refresh the in memory tree.
754 dir_node->EnsureChildExists(entry.name, file_id);
755 children_to_keep.insert(entry.name);
757 dir_node->ClearNonexistentChildren(children_to_keep);
759 success_callback.Run(normalized_file_list, false /*no more entries*/);
760 PendingRequestDone();
763 void MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile(
764 const base::File::Info& file_info,
765 const base::FilePath& snapshot_file_path) {
766 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
767 DCHECK(current_snapshot_request_info_.get());
768 current_snapshot_request_info_->success_callback.Run(
769 file_info, snapshot_file_path);
770 current_snapshot_request_info_.reset();
771 PendingRequestDone();
774 void MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError(
775 base::File::Error error) {
776 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
777 DCHECK(current_snapshot_request_info_.get());
778 current_snapshot_request_info_->error_callback.Run(error);
779 current_snapshot_request_info_.reset();
780 PendingRequestDone();
783 void MTPDeviceDelegateImplLinux::OnDidReadBytes(
784 const ReadBytesSuccessCallback& success_callback,
785 const base::File::Info& file_info, int bytes_read) {
786 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
787 success_callback.Run(file_info, bytes_read);
788 PendingRequestDone();
791 void MTPDeviceDelegateImplLinux::OnDidFillFileCache(
792 const base::FilePath& path,
793 const fileapi::AsyncFileUtil::EntryList& /* file_list */,
794 bool /* has_more */) {
795 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
796 DCHECK(path.IsParent(pending_tasks_.front().path));
797 pending_tasks_.front().cached_path = path;
800 void MTPDeviceDelegateImplLinux::OnFillFileCacheFailed(
801 base::File::Error /* error */) {
802 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
803 // When filling the cache fails for the task at the front of the queue, clear
804 // the path of the task so it will not try to do any more caching. Instead,
805 // the task will just run and fail the CachedPathToId() lookup.
806 pending_tasks_.front().path.clear();
809 void MTPDeviceDelegateImplLinux::HandleDeviceFileError(
810 const ErrorCallback& error_callback,
811 uint32 file_id,
812 base::File::Error error) {
813 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
815 FileIdToMTPFileNodeMap::iterator it = file_id_to_node_map_.find(file_id);
816 if (it != file_id_to_node_map_.end()) {
817 MTPFileNode* parent = it->second->parent();
818 if (parent) {
819 bool ret = parent->DeleteChild(file_id);
820 DCHECK(ret);
823 error_callback.Run(error);
824 PendingRequestDone();
827 base::FilePath MTPDeviceDelegateImplLinux::NextUncachedPathComponent(
828 const base::FilePath& path,
829 const base::FilePath& cached_path) const {
830 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
831 DCHECK(cached_path.empty() || cached_path.IsParent(path));
833 base::FilePath uncached_path;
834 std::string device_relpath = GetDeviceRelativePath(device_path_, path);
835 if (!device_relpath.empty() && device_relpath != kRootPath) {
836 uncached_path = device_path_;
837 std::vector<std::string> device_relpath_components;
838 base::SplitString(device_relpath, '/', &device_relpath_components);
839 DCHECK(!device_relpath_components.empty());
840 bool all_components_cached = true;
841 const MTPFileNode* current_node = root_node_.get();
842 for (size_t i = 0; i < device_relpath_components.size(); ++i) {
843 current_node = current_node->GetChild(device_relpath_components[i]);
844 if (!current_node) {
845 // With a cache miss, check if it is a genuine failure. If so, pretend
846 // the entire |path| is cached, so there is no further attempt to do
847 // more caching. The actual operation will then fail.
848 all_components_cached =
849 !cached_path.empty() && (uncached_path == cached_path);
850 break;
852 uncached_path = uncached_path.Append(device_relpath_components[i]);
854 if (all_components_cached)
855 uncached_path.clear();
857 return uncached_path;
860 void MTPDeviceDelegateImplLinux::FillFileCache(
861 const base::FilePath& uncached_path) {
862 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
863 DCHECK(task_in_progress_);
865 ReadDirectorySuccessCallback success_callback =
866 base::Bind(&MTPDeviceDelegateImplLinux::OnDidFillFileCache,
867 weak_ptr_factory_.GetWeakPtr(),
868 uncached_path);
869 ErrorCallback error_callback =
870 base::Bind(&MTPDeviceDelegateImplLinux::OnFillFileCacheFailed,
871 weak_ptr_factory_.GetWeakPtr());
872 ReadDirectoryInternal(uncached_path, success_callback, error_callback);
876 bool MTPDeviceDelegateImplLinux::CachedPathToId(const base::FilePath& path,
877 uint32* id) const {
878 DCHECK(id);
880 std::string device_relpath = GetDeviceRelativePath(device_path_, path);
881 if (device_relpath.empty())
882 return false;
883 std::vector<std::string> device_relpath_components;
884 if (device_relpath != kRootPath)
885 base::SplitString(device_relpath, '/', &device_relpath_components);
886 const MTPFileNode* current_node = root_node_.get();
887 for (size_t i = 0; i < device_relpath_components.size(); ++i) {
888 current_node = current_node->GetChild(device_relpath_components[i]);
889 if (!current_node)
890 return false;
892 *id = current_node->file_id();
893 return true;
896 void CreateMTPDeviceAsyncDelegate(
897 const std::string& device_location,
898 const CreateMTPDeviceAsyncDelegateCallback& callback) {
899 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
900 callback.Run(new MTPDeviceDelegateImplLinux(device_location));