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"
10 #include "base/bind.h"
11 #include "base/numerics/safe_conversions.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/string_util.h"
15 #include "chrome/browser/media_galleries/linux/mtp_device_task_helper.h"
16 #include "chrome/browser/media_galleries/linux/mtp_device_task_helper_map_service.h"
17 #include "chrome/browser/media_galleries/linux/snapshot_file_details.h"
18 #include "net/base/io_buffer.h"
19 #include "third_party/cros_system_api/dbus/service_constants.h"
23 // File path separator constant.
24 const char kRootPath[] = "/";
26 // Returns the device relative file path given |file_path|.
27 // E.g.: If the |file_path| is "/usb:2,2:12345/DCIM" and |registered_dev_path|
28 // is "/usb:2,2:12345", this function returns the device relative path which is
30 // In the special case when |registered_dev_path| and |file_path| are the same,
31 // return |kRootPath|.
32 std::string GetDeviceRelativePath(const base::FilePath& registered_dev_path,
33 const base::FilePath& file_path) {
34 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
35 DCHECK(!registered_dev_path.empty());
36 DCHECK(!file_path.empty());
38 if (registered_dev_path == file_path) {
41 base::FilePath relative_path;
42 if (registered_dev_path.AppendRelativePath(file_path, &relative_path)) {
43 DCHECK(!relative_path.empty());
44 result = relative_path.value();
50 // Returns the MTPDeviceTaskHelper object associated with the MTP device
53 // |storage_name| specifies the name of the storage device.
54 // Returns NULL if the |storage_name| is no longer valid (e.g. because the
55 // corresponding storage device is detached, etc).
56 MTPDeviceTaskHelper* GetDeviceTaskHelperForStorage(
57 const std::string& storage_name) {
58 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
59 return MTPDeviceTaskHelperMapService::GetInstance()->GetDeviceTaskHelper(
63 // Opens the storage device for communication.
65 // Called on the UI thread to dispatch the request to the
66 // MediaTransferProtocolManager.
68 // |storage_name| specifies the name of the storage device.
69 // |reply_callback| is called when the OpenStorage request completes.
70 // |reply_callback| runs on the IO thread.
71 void OpenStorageOnUIThread(
72 const std::string& storage_name,
73 const MTPDeviceTaskHelper::OpenStorageCallback& reply_callback) {
74 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
75 MTPDeviceTaskHelper* task_helper =
76 GetDeviceTaskHelperForStorage(storage_name);
79 MTPDeviceTaskHelperMapService::GetInstance()->CreateDeviceTaskHelper(
82 task_helper->OpenStorage(storage_name, reply_callback);
85 // Enumerates the |dir_id| directory file entries.
87 // Called on the UI thread to dispatch the request to the
88 // MediaTransferProtocolManager.
90 // |storage_name| specifies the name of the storage device.
91 // |success_callback| is called when the ReadDirectory request succeeds.
92 // |error_callback| is called when the ReadDirectory request fails.
93 // |success_callback| and |error_callback| runs on the IO thread.
94 void ReadDirectoryOnUIThread(
95 const std::string& storage_name,
97 const MTPDeviceTaskHelper::ReadDirectorySuccessCallback& success_callback,
98 const MTPDeviceTaskHelper::ErrorCallback& error_callback) {
99 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
100 MTPDeviceTaskHelper* task_helper =
101 GetDeviceTaskHelperForStorage(storage_name);
104 task_helper->ReadDirectory(dir_id, success_callback, error_callback);
107 // Gets the |file_path| details.
109 // Called on the UI thread to dispatch the request to the
110 // MediaTransferProtocolManager.
112 // |storage_name| specifies the name of the storage device.
113 // |success_callback| is called when the GetFileInfo request succeeds.
114 // |error_callback| is called when the GetFileInfo request fails.
115 // |success_callback| and |error_callback| runs on the IO thread.
116 void GetFileInfoOnUIThread(
117 const std::string& storage_name,
119 const MTPDeviceTaskHelper::GetFileInfoSuccessCallback& success_callback,
120 const MTPDeviceTaskHelper::ErrorCallback& error_callback) {
121 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
122 MTPDeviceTaskHelper* task_helper =
123 GetDeviceTaskHelperForStorage(storage_name);
126 task_helper->GetFileInfo(file_id, success_callback, error_callback);
129 // Copies the contents of |device_file_path| to |snapshot_file_path|.
131 // Called on the UI thread to dispatch the request to the
132 // MediaTransferProtocolManager.
134 // |storage_name| specifies the name of the storage device.
135 // |device_file_path| specifies the media device file path.
136 // |snapshot_file_path| specifies the platform path of the snapshot file.
137 // |file_size| specifies the number of bytes that will be written to the
139 // |success_callback| is called when the copy operation succeeds.
140 // |error_callback| is called when the copy operation fails.
141 // |success_callback| and |error_callback| runs on the IO thread.
142 void WriteDataIntoSnapshotFileOnUIThread(
143 const std::string& storage_name,
144 const SnapshotRequestInfo& request_info,
145 const base::File::Info& snapshot_file_info) {
146 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
147 MTPDeviceTaskHelper* task_helper =
148 GetDeviceTaskHelperForStorage(storage_name);
151 task_helper->WriteDataIntoSnapshotFile(request_info, snapshot_file_info);
154 // Copies the contents of |device_file_path| to |snapshot_file_path|.
156 // Called on the UI thread to dispatch the request to the
157 // MediaTransferProtocolManager.
159 // |storage_name| specifies the name of the storage device.
160 // |request| is a struct containing details about the byte read request.
161 void ReadBytesOnUIThread(
162 const std::string& storage_name,
163 const MTPDeviceAsyncDelegate::ReadBytesRequest& request) {
164 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
165 MTPDeviceTaskHelper* task_helper =
166 GetDeviceTaskHelperForStorage(storage_name);
169 task_helper->ReadBytes(request);
172 // Closes the device storage specified by the |storage_name| and destroys the
173 // MTPDeviceTaskHelper object associated with the device storage.
175 // Called on the UI thread to dispatch the request to the
176 // MediaTransferProtocolManager.
177 void CloseStorageAndDestroyTaskHelperOnUIThread(
178 const std::string& storage_name) {
179 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
180 MTPDeviceTaskHelper* task_helper =
181 GetDeviceTaskHelperForStorage(storage_name);
184 task_helper->CloseStorage();
185 MTPDeviceTaskHelperMapService::GetInstance()->DestroyDeviceTaskHelper(
191 MTPDeviceDelegateImplLinux::PendingTaskInfo::PendingTaskInfo(
192 const base::FilePath& path,
193 content::BrowserThread::ID thread_id,
194 const tracked_objects::Location& location,
195 const base::Closure& task)
197 thread_id(thread_id),
202 MTPDeviceDelegateImplLinux::PendingTaskInfo::~PendingTaskInfo() {
205 // Represents a file on the MTP device.
206 // Lives on the IO thread.
207 class MTPDeviceDelegateImplLinux::MTPFileNode {
209 MTPFileNode(uint32 file_id,
211 FileIdToMTPFileNodeMap* file_id_to_node_map);
214 const MTPFileNode* GetChild(const std::string& name) const;
216 void EnsureChildExists(const std::string& name, uint32 id);
218 // Clears all the children, except those in |children_to_keep|.
219 void ClearNonexistentChildren(
220 const std::set<std::string>& children_to_keep);
222 bool DeleteChild(uint32 file_id);
224 uint32 file_id() const { return file_id_; }
225 MTPFileNode* parent() { return parent_; }
228 // Container for holding a node's children.
229 typedef base::ScopedPtrHashMap<std::string, MTPFileNode> ChildNodes;
231 const uint32 file_id_;
232 ChildNodes children_;
233 MTPFileNode* const parent_;
234 FileIdToMTPFileNodeMap* file_id_to_node_map_;
236 DISALLOW_COPY_AND_ASSIGN(MTPFileNode);
239 MTPDeviceDelegateImplLinux::MTPFileNode::MTPFileNode(
242 FileIdToMTPFileNodeMap* file_id_to_node_map)
245 file_id_to_node_map_(file_id_to_node_map) {
246 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
247 DCHECK(file_id_to_node_map_);
248 DCHECK(!ContainsKey(*file_id_to_node_map_, file_id_));
249 (*file_id_to_node_map_)[file_id_] = this;
252 MTPDeviceDelegateImplLinux::MTPFileNode::~MTPFileNode() {
253 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
254 size_t erased = file_id_to_node_map_->erase(file_id_);
255 DCHECK_EQ(1U, erased);
258 const MTPDeviceDelegateImplLinux::MTPFileNode*
259 MTPDeviceDelegateImplLinux::MTPFileNode::GetChild(
260 const std::string& name) const {
261 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
262 return children_.get(name);
265 void MTPDeviceDelegateImplLinux::MTPFileNode::EnsureChildExists(
266 const std::string& name,
268 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
269 const MTPFileNode* child = GetChild(name);
270 if (child && child->file_id() == id)
275 make_scoped_ptr(new MTPFileNode(id, this, file_id_to_node_map_)));
278 void MTPDeviceDelegateImplLinux::MTPFileNode::ClearNonexistentChildren(
279 const std::set<std::string>& children_to_keep) {
280 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
281 std::set<std::string> children_to_erase;
282 for (ChildNodes::const_iterator it = children_.begin();
283 it != children_.end(); ++it) {
284 if (ContainsKey(children_to_keep, it->first))
286 children_to_erase.insert(it->first);
288 for (std::set<std::string>::iterator it = children_to_erase.begin();
289 it != children_to_erase.end(); ++it) {
290 children_.take_and_erase(*it);
294 bool MTPDeviceDelegateImplLinux::MTPFileNode::DeleteChild(uint32 file_id) {
295 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
296 for (ChildNodes::iterator it = children_.begin();
297 it != children_.end(); ++it) {
298 if (it->second->file_id() == file_id) {
306 MTPDeviceDelegateImplLinux::MTPDeviceDelegateImplLinux(
307 const std::string& device_location)
308 : init_state_(UNINITIALIZED),
309 task_in_progress_(false),
310 device_path_(device_location),
311 root_node_(new MTPFileNode(mtpd::kRootFileId,
313 &file_id_to_node_map_)),
314 weak_ptr_factory_(this) {
315 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
316 DCHECK(!device_path_.empty());
317 base::RemoveChars(device_location, kRootPath, &storage_name_);
318 DCHECK(!storage_name_.empty());
321 MTPDeviceDelegateImplLinux::~MTPDeviceDelegateImplLinux() {
322 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
325 void MTPDeviceDelegateImplLinux::GetFileInfo(
326 const base::FilePath& file_path,
327 const GetFileInfoSuccessCallback& success_callback,
328 const ErrorCallback& error_callback) {
329 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
330 DCHECK(!file_path.empty());
331 base::Closure closure =
332 base::Bind(&MTPDeviceDelegateImplLinux::GetFileInfoInternal,
333 weak_ptr_factory_.GetWeakPtr(),
337 EnsureInitAndRunTask(PendingTaskInfo(file_path,
338 content::BrowserThread::IO,
343 void MTPDeviceDelegateImplLinux::ReadDirectory(
344 const base::FilePath& root,
345 const ReadDirectorySuccessCallback& success_callback,
346 const ErrorCallback& error_callback) {
347 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
348 DCHECK(!root.empty());
349 base::Closure closure =
350 base::Bind(&MTPDeviceDelegateImplLinux::ReadDirectoryInternal,
351 weak_ptr_factory_.GetWeakPtr(),
355 EnsureInitAndRunTask(PendingTaskInfo(root,
356 content::BrowserThread::IO,
361 void MTPDeviceDelegateImplLinux::CreateSnapshotFile(
362 const base::FilePath& device_file_path,
363 const base::FilePath& local_path,
364 const CreateSnapshotFileSuccessCallback& success_callback,
365 const ErrorCallback& error_callback) {
366 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
367 DCHECK(!device_file_path.empty());
368 DCHECK(!local_path.empty());
369 base::Closure closure =
370 base::Bind(&MTPDeviceDelegateImplLinux::CreateSnapshotFileInternal,
371 weak_ptr_factory_.GetWeakPtr(),
376 EnsureInitAndRunTask(PendingTaskInfo(device_file_path,
377 content::BrowserThread::IO,
382 bool MTPDeviceDelegateImplLinux::IsStreaming() {
386 void MTPDeviceDelegateImplLinux::ReadBytes(
387 const base::FilePath& device_file_path,
388 net::IOBuffer* buf, int64 offset, int buf_len,
389 const ReadBytesSuccessCallback& success_callback,
390 const ErrorCallback& error_callback) {
391 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
392 DCHECK(!device_file_path.empty());
393 base::Closure closure =
394 base::Bind(&MTPDeviceDelegateImplLinux::ReadBytesInternal,
395 weak_ptr_factory_.GetWeakPtr(),
397 make_scoped_refptr(buf),
402 EnsureInitAndRunTask(PendingTaskInfo(device_file_path,
403 content::BrowserThread::IO,
408 void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() {
409 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
410 // To cancel all the pending tasks, destroy the MTPDeviceTaskHelper object.
411 content::BrowserThread::PostTask(
412 content::BrowserThread::UI,
414 base::Bind(&CloseStorageAndDestroyTaskHelperOnUIThread, storage_name_));
418 void MTPDeviceDelegateImplLinux::GetFileInfoInternal(
419 const base::FilePath& file_path,
420 const GetFileInfoSuccessCallback& success_callback,
421 const ErrorCallback& error_callback) {
422 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
425 if (CachedPathToId(file_path, &file_id)) {
426 GetFileInfoSuccessCallback success_callback_wrapper =
427 base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfo,
428 weak_ptr_factory_.GetWeakPtr(),
430 ErrorCallback error_callback_wrapper =
431 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
432 weak_ptr_factory_.GetWeakPtr(),
437 base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
440 success_callback_wrapper,
441 error_callback_wrapper);
442 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
443 content::BrowserThread::UI,
447 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
449 PendingRequestDone();
452 void MTPDeviceDelegateImplLinux::ReadDirectoryInternal(
453 const base::FilePath& root,
454 const ReadDirectorySuccessCallback& success_callback,
455 const ErrorCallback& error_callback) {
456 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
459 if (CachedPathToId(root, &dir_id)) {
460 GetFileInfoSuccessCallback success_callback_wrapper =
461 base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory,
462 weak_ptr_factory_.GetWeakPtr(),
466 ErrorCallback error_callback_wrapper =
467 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
468 weak_ptr_factory_.GetWeakPtr(),
471 base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
474 success_callback_wrapper,
475 error_callback_wrapper);
476 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
477 content::BrowserThread::UI,
481 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
483 PendingRequestDone();
486 void MTPDeviceDelegateImplLinux::CreateSnapshotFileInternal(
487 const base::FilePath& device_file_path,
488 const base::FilePath& local_path,
489 const CreateSnapshotFileSuccessCallback& success_callback,
490 const ErrorCallback& error_callback) {
491 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
494 if (CachedPathToId(device_file_path, &file_id)) {
495 scoped_ptr<SnapshotRequestInfo> request_info(
496 new SnapshotRequestInfo(file_id,
500 GetFileInfoSuccessCallback success_callback_wrapper =
502 &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile,
503 weak_ptr_factory_.GetWeakPtr(),
504 base::Passed(&request_info));
505 ErrorCallback error_callback_wrapper =
506 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
507 weak_ptr_factory_.GetWeakPtr(),
510 base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
513 success_callback_wrapper,
514 error_callback_wrapper);
515 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
516 content::BrowserThread::UI,
520 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
522 PendingRequestDone();
525 void MTPDeviceDelegateImplLinux::ReadBytesInternal(
526 const base::FilePath& device_file_path,
527 net::IOBuffer* buf, int64 offset, int buf_len,
528 const ReadBytesSuccessCallback& success_callback,
529 const ErrorCallback& error_callback) {
530 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
533 if (CachedPathToId(device_file_path, &file_id)) {
534 ReadBytesRequest request(
535 file_id, buf, offset, buf_len,
536 base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadBytes,
537 weak_ptr_factory_.GetWeakPtr(),
539 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
540 weak_ptr_factory_.GetWeakPtr(),
544 base::Closure closure =
545 base::Bind(base::Bind(&ReadBytesOnUIThread, storage_name_, request));
546 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
547 content::BrowserThread::UI,
551 error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
553 PendingRequestDone();
556 void MTPDeviceDelegateImplLinux::EnsureInitAndRunTask(
557 const PendingTaskInfo& task_info) {
558 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
559 if ((init_state_ == INITIALIZED) && !task_in_progress_) {
564 // Only *Internal functions have empty paths. Since they are the continuation
565 // of the current running task, they get to cut in line.
566 if (task_info.path.empty())
567 pending_tasks_.push_front(task_info);
569 pending_tasks_.push_back(task_info);
571 if (init_state_ == UNINITIALIZED) {
572 init_state_ = PENDING_INIT;
573 task_in_progress_ = true;
574 content::BrowserThread::PostTask(
575 content::BrowserThread::UI,
577 base::Bind(&OpenStorageOnUIThread,
579 base::Bind(&MTPDeviceDelegateImplLinux::OnInitCompleted,
580 weak_ptr_factory_.GetWeakPtr())));
584 void MTPDeviceDelegateImplLinux::RunTask(const PendingTaskInfo& task_info) {
585 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
586 DCHECK_EQ(INITIALIZED, init_state_);
587 DCHECK(!task_in_progress_);
588 task_in_progress_ = true;
590 bool need_to_check_cache = !task_info.path.empty();
591 if (need_to_check_cache) {
592 base::FilePath uncached_path =
593 NextUncachedPathComponent(task_info.path, task_info.cached_path);
594 if (!uncached_path.empty()) {
595 // Save the current task and do a cache lookup first.
596 pending_tasks_.push_front(task_info);
597 FillFileCache(uncached_path);
602 content::BrowserThread::PostTask(task_info.thread_id,
607 void MTPDeviceDelegateImplLinux::WriteDataIntoSnapshotFile(
608 const base::File::Info& file_info) {
609 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
610 DCHECK(current_snapshot_request_info_.get());
611 DCHECK_GT(file_info.size, 0);
612 DCHECK(task_in_progress_);
613 SnapshotRequestInfo request_info(
614 current_snapshot_request_info_->file_id,
615 current_snapshot_request_info_->snapshot_file_path,
617 &MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile,
618 weak_ptr_factory_.GetWeakPtr()),
620 &MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError,
621 weak_ptr_factory_.GetWeakPtr()));
623 base::Closure task_closure = base::Bind(&WriteDataIntoSnapshotFileOnUIThread,
627 content::BrowserThread::PostTask(content::BrowserThread::UI,
632 void MTPDeviceDelegateImplLinux::PendingRequestDone() {
633 DCHECK(task_in_progress_);
634 task_in_progress_ = false;
635 ProcessNextPendingRequest();
638 void MTPDeviceDelegateImplLinux::ProcessNextPendingRequest() {
639 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
640 DCHECK(!task_in_progress_);
641 if (pending_tasks_.empty())
644 PendingTaskInfo task_info = pending_tasks_.front();
645 pending_tasks_.pop_front();
649 void MTPDeviceDelegateImplLinux::OnInitCompleted(bool succeeded) {
650 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
651 init_state_ = succeeded ? INITIALIZED : UNINITIALIZED;
652 PendingRequestDone();
655 void MTPDeviceDelegateImplLinux::OnDidGetFileInfo(
656 const GetFileInfoSuccessCallback& success_callback,
657 const base::File::Info& file_info) {
658 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
659 success_callback.Run(file_info);
660 PendingRequestDone();
663 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory(
665 const ReadDirectorySuccessCallback& success_callback,
666 const ErrorCallback& error_callback,
667 const base::File::Info& file_info) {
668 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
669 DCHECK(task_in_progress_);
670 if (!file_info.is_directory) {
671 return HandleDeviceFileError(error_callback,
673 base::File::FILE_ERROR_NOT_A_DIRECTORY);
676 base::Closure task_closure =
677 base::Bind(&ReadDirectoryOnUIThread,
680 base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadDirectory,
681 weak_ptr_factory_.GetWeakPtr(),
684 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
685 weak_ptr_factory_.GetWeakPtr(),
688 content::BrowserThread::PostTask(content::BrowserThread::UI,
693 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile(
694 scoped_ptr<SnapshotRequestInfo> snapshot_request_info,
695 const base::File::Info& file_info) {
696 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
697 DCHECK(!current_snapshot_request_info_.get());
698 DCHECK(snapshot_request_info.get());
699 DCHECK(task_in_progress_);
700 base::File::Error error = base::File::FILE_OK;
701 if (file_info.is_directory)
702 error = base::File::FILE_ERROR_NOT_A_FILE;
703 else if (file_info.size < 0 || file_info.size > kuint32max)
704 error = base::File::FILE_ERROR_FAILED;
706 if (error != base::File::FILE_OK)
707 return HandleDeviceFileError(snapshot_request_info->error_callback,
708 snapshot_request_info->file_id,
711 base::File::Info snapshot_file_info(file_info);
712 // Modify the last modified time to null. This prevents the time stamp
713 // verfication in LocalFileStreamReader.
714 snapshot_file_info.last_modified = base::Time();
716 current_snapshot_request_info_.reset(snapshot_request_info.release());
717 if (file_info.size == 0) {
718 // Empty snapshot file.
719 return OnDidWriteDataIntoSnapshotFile(
720 snapshot_file_info, current_snapshot_request_info_->snapshot_file_path);
722 WriteDataIntoSnapshotFile(snapshot_file_info);
725 void MTPDeviceDelegateImplLinux::OnDidReadDirectory(
727 const ReadDirectorySuccessCallback& success_callback,
728 const fileapi::AsyncFileUtil::EntryList& file_list,
730 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
732 FileIdToMTPFileNodeMap::iterator it = file_id_to_node_map_.find(dir_id);
733 DCHECK(it != file_id_to_node_map_.end());
734 MTPFileNode* dir_node = it->second;
736 fileapi::AsyncFileUtil::EntryList normalized_file_list;
737 for (size_t i = 0; i < file_list.size(); ++i) {
738 normalized_file_list.push_back(file_list[i]);
739 fileapi::DirectoryEntry& entry = normalized_file_list.back();
741 // |entry.name| has the file id encoded in it. Decode here.
742 size_t separator_idx = entry.name.find_last_of(',');
743 DCHECK_NE(std::string::npos, separator_idx);
744 std::string file_id_str = entry.name.substr(separator_idx);
745 file_id_str = file_id_str.substr(1); // Get rid of the comma.
747 bool ret = base::StringToUint(file_id_str, &file_id);
749 entry.name = entry.name.substr(0, separator_idx);
751 // Refresh the in memory tree.
752 dir_node->EnsureChildExists(entry.name, file_id);
753 child_nodes_seen_.insert(entry.name);
756 success_callback.Run(normalized_file_list, has_more);
758 return; // Wait to be called again.
760 // Last call, finish book keeping and continue with the next request.
761 dir_node->ClearNonexistentChildren(child_nodes_seen_);
762 child_nodes_seen_.clear();
764 PendingRequestDone();
767 void MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile(
768 const base::File::Info& file_info,
769 const base::FilePath& snapshot_file_path) {
770 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
771 DCHECK(current_snapshot_request_info_.get());
772 current_snapshot_request_info_->success_callback.Run(
773 file_info, snapshot_file_path);
774 current_snapshot_request_info_.reset();
775 PendingRequestDone();
778 void MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError(
779 base::File::Error error) {
780 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
781 DCHECK(current_snapshot_request_info_.get());
782 current_snapshot_request_info_->error_callback.Run(error);
783 current_snapshot_request_info_.reset();
784 PendingRequestDone();
787 void MTPDeviceDelegateImplLinux::OnDidReadBytes(
788 const ReadBytesSuccessCallback& success_callback,
789 const base::File::Info& file_info, int bytes_read) {
790 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
791 success_callback.Run(file_info, bytes_read);
792 PendingRequestDone();
795 void MTPDeviceDelegateImplLinux::OnDidFillFileCache(
796 const base::FilePath& path,
797 const fileapi::AsyncFileUtil::EntryList& /* file_list */,
799 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
800 DCHECK(path.IsParent(pending_tasks_.front().path));
802 return; // Wait until all entries have been read.
803 pending_tasks_.front().cached_path = path;
806 void MTPDeviceDelegateImplLinux::OnFillFileCacheFailed(
807 base::File::Error /* error */) {
808 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
809 // When filling the cache fails for the task at the front of the queue, clear
810 // the path of the task so it will not try to do any more caching. Instead,
811 // the task will just run and fail the CachedPathToId() lookup.
812 pending_tasks_.front().path.clear();
815 void MTPDeviceDelegateImplLinux::HandleDeviceFileError(
816 const ErrorCallback& error_callback,
818 base::File::Error error) {
819 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
821 FileIdToMTPFileNodeMap::iterator it = file_id_to_node_map_.find(file_id);
822 if (it != file_id_to_node_map_.end()) {
823 MTPFileNode* parent = it->second->parent();
825 bool ret = parent->DeleteChild(file_id);
829 error_callback.Run(error);
830 PendingRequestDone();
833 base::FilePath MTPDeviceDelegateImplLinux::NextUncachedPathComponent(
834 const base::FilePath& path,
835 const base::FilePath& cached_path) const {
836 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
837 DCHECK(cached_path.empty() || cached_path.IsParent(path));
839 base::FilePath uncached_path;
840 std::string device_relpath = GetDeviceRelativePath(device_path_, path);
841 if (!device_relpath.empty() && device_relpath != kRootPath) {
842 uncached_path = device_path_;
843 std::vector<std::string> device_relpath_components;
844 base::SplitString(device_relpath, '/', &device_relpath_components);
845 DCHECK(!device_relpath_components.empty());
846 bool all_components_cached = true;
847 const MTPFileNode* current_node = root_node_.get();
848 for (size_t i = 0; i < device_relpath_components.size(); ++i) {
849 current_node = current_node->GetChild(device_relpath_components[i]);
851 // With a cache miss, check if it is a genuine failure. If so, pretend
852 // the entire |path| is cached, so there is no further attempt to do
853 // more caching. The actual operation will then fail.
854 all_components_cached =
855 !cached_path.empty() && (uncached_path == cached_path);
858 uncached_path = uncached_path.Append(device_relpath_components[i]);
860 if (all_components_cached)
861 uncached_path.clear();
863 return uncached_path;
866 void MTPDeviceDelegateImplLinux::FillFileCache(
867 const base::FilePath& uncached_path) {
868 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
869 DCHECK(task_in_progress_);
871 ReadDirectorySuccessCallback success_callback =
872 base::Bind(&MTPDeviceDelegateImplLinux::OnDidFillFileCache,
873 weak_ptr_factory_.GetWeakPtr(),
875 ErrorCallback error_callback =
876 base::Bind(&MTPDeviceDelegateImplLinux::OnFillFileCacheFailed,
877 weak_ptr_factory_.GetWeakPtr());
878 ReadDirectoryInternal(uncached_path, success_callback, error_callback);
882 bool MTPDeviceDelegateImplLinux::CachedPathToId(const base::FilePath& path,
886 std::string device_relpath = GetDeviceRelativePath(device_path_, path);
887 if (device_relpath.empty())
889 std::vector<std::string> device_relpath_components;
890 if (device_relpath != kRootPath)
891 base::SplitString(device_relpath, '/', &device_relpath_components);
892 const MTPFileNode* current_node = root_node_.get();
893 for (size_t i = 0; i < device_relpath_components.size(); ++i) {
894 current_node = current_node->GetChild(device_relpath_components[i]);
898 *id = current_node->file_id();
902 void CreateMTPDeviceAsyncDelegate(
903 const std::string& device_location,
904 const CreateMTPDeviceAsyncDelegateCallback& callback) {
905 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
906 callback.Run(new MTPDeviceDelegateImplLinux(device_location));