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"
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"
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
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());
39 if (registered_dev_path
== file_path
) {
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();
51 // Returns the MTPDeviceTaskHelper object associated with the MTP device
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(
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
);
80 MTPDeviceTaskHelperMapService::GetInstance()->CreateDeviceTaskHelper(
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
,
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
);
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
,
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
);
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
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
);
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
);
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
);
186 task_helper
->CloseStorage();
187 MTPDeviceTaskHelperMapService::GetInstance()->DestroyDeviceTaskHelper(
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
)
199 thread_id(thread_id
),
204 MTPDeviceDelegateImplLinux::PendingTaskInfo::~PendingTaskInfo() {
207 // Represents a file on the MTP device.
208 // Lives on the IO thread.
209 class MTPDeviceDelegateImplLinux::MTPFileNode
{
211 MTPFileNode(uint32 file_id
,
213 FileIdToMTPFileNodeMap
* file_id_to_node_map
);
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_
; }
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(
244 FileIdToMTPFileNodeMap
* file_id_to_node_map
)
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
,
270 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
271 const MTPFileNode
* child
= GetChild(name
);
272 if (child
&& child
->file_id() == id
)
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
))
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
) {
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
,
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(),
339 EnsureInitAndRunTask(PendingTaskInfo(file_path
,
340 content::BrowserThread::IO
,
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(),
357 EnsureInitAndRunTask(PendingTaskInfo(root
,
358 content::BrowserThread::IO
,
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(),
378 EnsureInitAndRunTask(PendingTaskInfo(device_file_path
,
379 content::BrowserThread::IO
,
384 bool MTPDeviceDelegateImplLinux::IsStreaming() {
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(),
399 make_scoped_refptr(buf
),
404 EnsureInitAndRunTask(PendingTaskInfo(device_file_path
,
405 content::BrowserThread::IO
,
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
,
416 base::Bind(&CloseStorageAndDestroyTaskHelperOnUIThread
, storage_name_
));
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
);
427 if (CachedPathToId(file_path
, &file_id
)) {
428 GetFileInfoSuccessCallback success_callback_wrapper
=
429 base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfo
,
430 weak_ptr_factory_
.GetWeakPtr(),
432 ErrorCallback error_callback_wrapper
=
433 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
434 weak_ptr_factory_
.GetWeakPtr(),
439 base::Closure closure
= base::Bind(&GetFileInfoOnUIThread
,
442 success_callback_wrapper
,
443 error_callback_wrapper
);
444 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
445 content::BrowserThread::UI
,
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
);
461 if (CachedPathToId(root
, &dir_id
)) {
462 GetFileInfoSuccessCallback success_callback_wrapper
=
463 base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory
,
464 weak_ptr_factory_
.GetWeakPtr(),
468 ErrorCallback error_callback_wrapper
=
469 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
470 weak_ptr_factory_
.GetWeakPtr(),
473 base::Closure closure
= base::Bind(&GetFileInfoOnUIThread
,
476 success_callback_wrapper
,
477 error_callback_wrapper
);
478 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
479 content::BrowserThread::UI
,
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
);
496 if (CachedPathToId(device_file_path
, &file_id
)) {
497 scoped_ptr
<SnapshotRequestInfo
> request_info(
498 new SnapshotRequestInfo(file_id
,
502 GetFileInfoSuccessCallback success_callback_wrapper
=
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(),
512 base::Closure closure
= base::Bind(&GetFileInfoOnUIThread
,
515 success_callback_wrapper
,
516 error_callback_wrapper
);
517 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
518 content::BrowserThread::UI
,
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
);
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(),
541 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
542 weak_ptr_factory_
.GetWeakPtr(),
546 base::Closure closure
=
547 base::Bind(base::Bind(&ReadBytesOnUIThread
, storage_name_
, request
));
548 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
549 content::BrowserThread::UI
,
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_
) {
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
);
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
,
579 base::Bind(&OpenStorageOnUIThread
,
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
);
604 content::BrowserThread::PostTask(task_info
.thread_id
,
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
,
619 &MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile
,
620 weak_ptr_factory_
.GetWeakPtr()),
622 &MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError
,
623 weak_ptr_factory_
.GetWeakPtr()));
625 base::Closure task_closure
= base::Bind(&WriteDataIntoSnapshotFileOnUIThread
,
629 content::BrowserThread::PostTask(content::BrowserThread::UI
,
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())
646 PendingTaskInfo task_info
= pending_tasks_
.front();
647 pending_tasks_
.pop_front();
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(
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
,
675 base::File::FILE_ERROR_NOT_A_DIRECTORY
);
678 base::Closure task_closure
=
679 base::Bind(&ReadDirectoryOnUIThread
,
682 base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadDirectory
,
683 weak_ptr_factory_
.GetWeakPtr(),
686 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
687 weak_ptr_factory_
.GetWeakPtr(),
690 content::BrowserThread::PostTask(content::BrowserThread::UI
,
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
,
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(
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.
749 bool ret
= base::StringToUint(file_id_str
, &file_id
);
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
,
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();
819 bool ret
= parent
->DeleteChild(file_id
);
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
]);
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
);
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(),
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
,
880 std::string device_relpath
= GetDeviceRelativePath(device_path_
, path
);
881 if (device_relpath
.empty())
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
]);
892 *id
= current_node
->file_id();
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
));