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
,
210 const std::string
& file_name
,
212 FileIdToMTPFileNodeMap
* file_id_to_node_map
);
215 const MTPFileNode
* GetChild(const std::string
& name
) const;
217 void EnsureChildExists(const std::string
& name
, uint32 id
);
219 // Clears all the children, except those in |children_to_keep|.
220 void ClearNonexistentChildren(
221 const std::set
<std::string
>& children_to_keep
);
223 bool DeleteChild(uint32 file_id
);
225 uint32
file_id() const { return file_id_
; }
226 const std::string
& file_name() const { return file_name_
; }
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 const std::string file_name_
;
236 ChildNodes children_
;
237 MTPFileNode
* const parent_
;
238 FileIdToMTPFileNodeMap
* file_id_to_node_map_
;
240 DISALLOW_COPY_AND_ASSIGN(MTPFileNode
);
243 MTPDeviceDelegateImplLinux::MTPFileNode::MTPFileNode(
245 const std::string
& file_name
,
247 FileIdToMTPFileNodeMap
* file_id_to_node_map
)
249 file_name_(file_name
),
251 file_id_to_node_map_(file_id_to_node_map
) {
252 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
253 DCHECK(file_id_to_node_map_
);
254 DCHECK(!ContainsKey(*file_id_to_node_map_
, file_id_
));
255 (*file_id_to_node_map_
)[file_id_
] = this;
258 MTPDeviceDelegateImplLinux::MTPFileNode::~MTPFileNode() {
259 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
260 size_t erased
= file_id_to_node_map_
->erase(file_id_
);
261 DCHECK_EQ(1U, erased
);
264 const MTPDeviceDelegateImplLinux::MTPFileNode
*
265 MTPDeviceDelegateImplLinux::MTPFileNode::GetChild(
266 const std::string
& name
) const {
267 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
268 return children_
.get(name
);
271 void MTPDeviceDelegateImplLinux::MTPFileNode::EnsureChildExists(
272 const std::string
& name
,
274 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
275 const MTPFileNode
* child
= GetChild(name
);
276 if (child
&& child
->file_id() == id
)
281 make_scoped_ptr(new MTPFileNode(id
, name
, this, file_id_to_node_map_
)));
284 void MTPDeviceDelegateImplLinux::MTPFileNode::ClearNonexistentChildren(
285 const std::set
<std::string
>& children_to_keep
) {
286 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
287 std::set
<std::string
> children_to_erase
;
288 for (ChildNodes::const_iterator it
= children_
.begin();
289 it
!= children_
.end(); ++it
) {
290 if (ContainsKey(children_to_keep
, it
->first
))
292 children_to_erase
.insert(it
->first
);
294 for (std::set
<std::string
>::iterator it
= children_to_erase
.begin();
295 it
!= children_to_erase
.end(); ++it
) {
296 children_
.take_and_erase(*it
);
300 bool MTPDeviceDelegateImplLinux::MTPFileNode::DeleteChild(uint32 file_id
) {
301 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
302 for (ChildNodes::iterator it
= children_
.begin();
303 it
!= children_
.end(); ++it
) {
304 if (it
->second
->file_id() == file_id
) {
312 MTPDeviceDelegateImplLinux::MTPDeviceDelegateImplLinux(
313 const std::string
& device_location
)
314 : init_state_(UNINITIALIZED
),
315 task_in_progress_(false),
316 device_path_(device_location
),
317 root_node_(new MTPFileNode(mtpd::kRootFileId
,
318 "", // Root node has no name.
319 NULL
, // And no parent node.
320 &file_id_to_node_map_
)),
321 weak_ptr_factory_(this) {
322 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
323 DCHECK(!device_path_
.empty());
324 base::RemoveChars(device_location
, kRootPath
, &storage_name_
);
325 DCHECK(!storage_name_
.empty());
328 MTPDeviceDelegateImplLinux::~MTPDeviceDelegateImplLinux() {
329 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
332 void MTPDeviceDelegateImplLinux::GetFileInfo(
333 const base::FilePath
& file_path
,
334 const GetFileInfoSuccessCallback
& success_callback
,
335 const ErrorCallback
& error_callback
) {
336 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
337 DCHECK(!file_path
.empty());
339 // If a ReadDirectory operation is in progress, the file info may already be
341 FileInfoCache::const_iterator it
= file_info_cache_
.find(file_path
);
342 if (it
!= file_info_cache_
.end()) {
343 // TODO(thestig): This code is repeated in several places. Combine them.
344 // e.g. c/b/media_galleries/win/mtp_device_operations_util.cc
345 const storage::DirectoryEntry
& cached_file_entry
= it
->second
;
346 base::File::Info info
;
347 info
.size
= cached_file_entry
.size
;
348 info
.is_directory
= cached_file_entry
.is_directory
;
349 info
.is_symbolic_link
= false;
350 info
.last_modified
= cached_file_entry
.last_modified_time
;
351 info
.creation_time
= base::Time();
353 success_callback
.Run(info
);
356 base::Closure closure
=
357 base::Bind(&MTPDeviceDelegateImplLinux::GetFileInfoInternal
,
358 weak_ptr_factory_
.GetWeakPtr(),
362 EnsureInitAndRunTask(PendingTaskInfo(file_path
,
363 content::BrowserThread::IO
,
368 void MTPDeviceDelegateImplLinux::ReadDirectory(
369 const base::FilePath
& root
,
370 const ReadDirectorySuccessCallback
& success_callback
,
371 const ErrorCallback
& error_callback
) {
372 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
373 DCHECK(!root
.empty());
374 base::Closure closure
=
375 base::Bind(&MTPDeviceDelegateImplLinux::ReadDirectoryInternal
,
376 weak_ptr_factory_
.GetWeakPtr(),
380 EnsureInitAndRunTask(PendingTaskInfo(root
,
381 content::BrowserThread::IO
,
386 void MTPDeviceDelegateImplLinux::CreateSnapshotFile(
387 const base::FilePath
& device_file_path
,
388 const base::FilePath
& local_path
,
389 const CreateSnapshotFileSuccessCallback
& success_callback
,
390 const ErrorCallback
& error_callback
) {
391 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
392 DCHECK(!device_file_path
.empty());
393 DCHECK(!local_path
.empty());
394 base::Closure closure
=
395 base::Bind(&MTPDeviceDelegateImplLinux::CreateSnapshotFileInternal
,
396 weak_ptr_factory_
.GetWeakPtr(),
401 EnsureInitAndRunTask(PendingTaskInfo(device_file_path
,
402 content::BrowserThread::IO
,
407 bool MTPDeviceDelegateImplLinux::IsStreaming() {
411 void MTPDeviceDelegateImplLinux::ReadBytes(
412 const base::FilePath
& device_file_path
,
413 const scoped_refptr
<net::IOBuffer
>& buf
,
416 const ReadBytesSuccessCallback
& success_callback
,
417 const ErrorCallback
& error_callback
) {
418 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
419 DCHECK(!device_file_path
.empty());
420 base::Closure closure
=
421 base::Bind(&MTPDeviceDelegateImplLinux::ReadBytesInternal
,
422 weak_ptr_factory_
.GetWeakPtr(),
429 EnsureInitAndRunTask(PendingTaskInfo(device_file_path
,
430 content::BrowserThread::IO
,
435 void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() {
436 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
437 // To cancel all the pending tasks, destroy the MTPDeviceTaskHelper object.
438 content::BrowserThread::PostTask(
439 content::BrowserThread::UI
,
441 base::Bind(&CloseStorageAndDestroyTaskHelperOnUIThread
, storage_name_
));
445 void MTPDeviceDelegateImplLinux::GetFileInfoInternal(
446 const base::FilePath
& file_path
,
447 const GetFileInfoSuccessCallback
& success_callback
,
448 const ErrorCallback
& error_callback
) {
449 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
452 if (CachedPathToId(file_path
, &file_id
)) {
453 GetFileInfoSuccessCallback success_callback_wrapper
=
454 base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfo
,
455 weak_ptr_factory_
.GetWeakPtr(),
457 ErrorCallback error_callback_wrapper
=
458 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
459 weak_ptr_factory_
.GetWeakPtr(),
464 base::Closure closure
= base::Bind(&GetFileInfoOnUIThread
,
467 success_callback_wrapper
,
468 error_callback_wrapper
);
469 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
470 content::BrowserThread::UI
,
474 error_callback
.Run(base::File::FILE_ERROR_NOT_FOUND
);
476 PendingRequestDone();
479 void MTPDeviceDelegateImplLinux::ReadDirectoryInternal(
480 const base::FilePath
& root
,
481 const ReadDirectorySuccessCallback
& success_callback
,
482 const ErrorCallback
& error_callback
) {
483 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
486 if (CachedPathToId(root
, &dir_id
)) {
487 GetFileInfoSuccessCallback success_callback_wrapper
=
488 base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory
,
489 weak_ptr_factory_
.GetWeakPtr(),
493 ErrorCallback error_callback_wrapper
=
494 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
495 weak_ptr_factory_
.GetWeakPtr(),
498 base::Closure closure
= base::Bind(&GetFileInfoOnUIThread
,
501 success_callback_wrapper
,
502 error_callback_wrapper
);
503 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
504 content::BrowserThread::UI
,
508 error_callback
.Run(base::File::FILE_ERROR_NOT_FOUND
);
510 PendingRequestDone();
513 void MTPDeviceDelegateImplLinux::CreateSnapshotFileInternal(
514 const base::FilePath
& device_file_path
,
515 const base::FilePath
& local_path
,
516 const CreateSnapshotFileSuccessCallback
& success_callback
,
517 const ErrorCallback
& error_callback
) {
518 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
521 if (CachedPathToId(device_file_path
, &file_id
)) {
522 scoped_ptr
<SnapshotRequestInfo
> request_info(
523 new SnapshotRequestInfo(file_id
,
527 GetFileInfoSuccessCallback success_callback_wrapper
=
529 &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile
,
530 weak_ptr_factory_
.GetWeakPtr(),
531 base::Passed(&request_info
));
532 ErrorCallback error_callback_wrapper
=
533 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
534 weak_ptr_factory_
.GetWeakPtr(),
537 base::Closure closure
= base::Bind(&GetFileInfoOnUIThread
,
540 success_callback_wrapper
,
541 error_callback_wrapper
);
542 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
543 content::BrowserThread::UI
,
547 error_callback
.Run(base::File::FILE_ERROR_NOT_FOUND
);
549 PendingRequestDone();
552 void MTPDeviceDelegateImplLinux::ReadBytesInternal(
553 const base::FilePath
& device_file_path
,
554 net::IOBuffer
* buf
, int64 offset
, int buf_len
,
555 const ReadBytesSuccessCallback
& success_callback
,
556 const ErrorCallback
& error_callback
) {
557 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
560 if (CachedPathToId(device_file_path
, &file_id
)) {
561 ReadBytesRequest
request(
562 file_id
, buf
, offset
, buf_len
,
563 base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadBytes
,
564 weak_ptr_factory_
.GetWeakPtr(),
566 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
567 weak_ptr_factory_
.GetWeakPtr(),
571 base::Closure closure
=
572 base::Bind(base::Bind(&ReadBytesOnUIThread
, storage_name_
, request
));
573 EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
574 content::BrowserThread::UI
,
578 error_callback
.Run(base::File::FILE_ERROR_NOT_FOUND
);
580 PendingRequestDone();
583 void MTPDeviceDelegateImplLinux::EnsureInitAndRunTask(
584 const PendingTaskInfo
& task_info
) {
585 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
586 if ((init_state_
== INITIALIZED
) && !task_in_progress_
) {
591 // Only *Internal functions have empty paths. Since they are the continuation
592 // of the current running task, they get to cut in line.
593 if (task_info
.path
.empty())
594 pending_tasks_
.push_front(task_info
);
596 pending_tasks_
.push_back(task_info
);
598 if (init_state_
== UNINITIALIZED
) {
599 init_state_
= PENDING_INIT
;
600 task_in_progress_
= true;
601 content::BrowserThread::PostTask(
602 content::BrowserThread::UI
,
604 base::Bind(&OpenStorageOnUIThread
,
606 base::Bind(&MTPDeviceDelegateImplLinux::OnInitCompleted
,
607 weak_ptr_factory_
.GetWeakPtr())));
611 void MTPDeviceDelegateImplLinux::RunTask(const PendingTaskInfo
& task_info
) {
612 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
613 DCHECK_EQ(INITIALIZED
, init_state_
);
614 DCHECK(!task_in_progress_
);
615 task_in_progress_
= true;
617 bool need_to_check_cache
= !task_info
.path
.empty();
618 if (need_to_check_cache
) {
619 base::FilePath uncached_path
=
620 NextUncachedPathComponent(task_info
.path
, task_info
.cached_path
);
621 if (!uncached_path
.empty()) {
622 // Save the current task and do a cache lookup first.
623 pending_tasks_
.push_front(task_info
);
624 FillFileCache(uncached_path
);
629 content::BrowserThread::PostTask(task_info
.thread_id
,
634 void MTPDeviceDelegateImplLinux::WriteDataIntoSnapshotFile(
635 const base::File::Info
& file_info
) {
636 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
637 DCHECK(current_snapshot_request_info_
.get());
638 DCHECK_GT(file_info
.size
, 0);
639 DCHECK(task_in_progress_
);
640 SnapshotRequestInfo
request_info(
641 current_snapshot_request_info_
->file_id
,
642 current_snapshot_request_info_
->snapshot_file_path
,
644 &MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile
,
645 weak_ptr_factory_
.GetWeakPtr()),
647 &MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError
,
648 weak_ptr_factory_
.GetWeakPtr()));
650 base::Closure task_closure
= base::Bind(&WriteDataIntoSnapshotFileOnUIThread
,
654 content::BrowserThread::PostTask(content::BrowserThread::UI
,
659 void MTPDeviceDelegateImplLinux::PendingRequestDone() {
660 DCHECK(task_in_progress_
);
661 task_in_progress_
= false;
662 ProcessNextPendingRequest();
665 void MTPDeviceDelegateImplLinux::ProcessNextPendingRequest() {
666 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
667 DCHECK(!task_in_progress_
);
668 if (pending_tasks_
.empty())
671 PendingTaskInfo task_info
= pending_tasks_
.front();
672 pending_tasks_
.pop_front();
676 void MTPDeviceDelegateImplLinux::OnInitCompleted(bool succeeded
) {
677 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
678 init_state_
= succeeded
? INITIALIZED
: UNINITIALIZED
;
679 PendingRequestDone();
682 void MTPDeviceDelegateImplLinux::OnDidGetFileInfo(
683 const GetFileInfoSuccessCallback
& success_callback
,
684 const base::File::Info
& file_info
) {
685 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
686 success_callback
.Run(file_info
);
687 PendingRequestDone();
690 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory(
692 const ReadDirectorySuccessCallback
& success_callback
,
693 const ErrorCallback
& error_callback
,
694 const base::File::Info
& file_info
) {
695 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
696 DCHECK(task_in_progress_
);
697 if (!file_info
.is_directory
) {
698 return HandleDeviceFileError(error_callback
,
700 base::File::FILE_ERROR_NOT_A_DIRECTORY
);
703 base::Closure task_closure
=
704 base::Bind(&ReadDirectoryOnUIThread
,
707 base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadDirectory
,
708 weak_ptr_factory_
.GetWeakPtr(),
711 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
712 weak_ptr_factory_
.GetWeakPtr(),
715 content::BrowserThread::PostTask(content::BrowserThread::UI
,
720 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile(
721 scoped_ptr
<SnapshotRequestInfo
> snapshot_request_info
,
722 const base::File::Info
& file_info
) {
723 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
724 DCHECK(!current_snapshot_request_info_
.get());
725 DCHECK(snapshot_request_info
.get());
726 DCHECK(task_in_progress_
);
727 base::File::Error error
= base::File::FILE_OK
;
728 if (file_info
.is_directory
)
729 error
= base::File::FILE_ERROR_NOT_A_FILE
;
730 else if (file_info
.size
< 0 || file_info
.size
> kuint32max
)
731 error
= base::File::FILE_ERROR_FAILED
;
733 if (error
!= base::File::FILE_OK
)
734 return HandleDeviceFileError(snapshot_request_info
->error_callback
,
735 snapshot_request_info
->file_id
,
738 base::File::Info
snapshot_file_info(file_info
);
739 // Modify the last modified time to null. This prevents the time stamp
740 // verfication in LocalFileStreamReader.
741 snapshot_file_info
.last_modified
= base::Time();
743 current_snapshot_request_info_
.reset(snapshot_request_info
.release());
744 if (file_info
.size
== 0) {
745 // Empty snapshot file.
746 return OnDidWriteDataIntoSnapshotFile(
747 snapshot_file_info
, current_snapshot_request_info_
->snapshot_file_path
);
749 WriteDataIntoSnapshotFile(snapshot_file_info
);
752 void MTPDeviceDelegateImplLinux::OnDidReadDirectory(
754 const ReadDirectorySuccessCallback
& success_callback
,
755 const storage::AsyncFileUtil::EntryList
& file_list
,
757 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
759 FileIdToMTPFileNodeMap::iterator it
= file_id_to_node_map_
.find(dir_id
);
760 DCHECK(it
!= file_id_to_node_map_
.end());
761 MTPFileNode
* dir_node
= it
->second
;
763 // Traverse the MTPFileNode tree to reconstuct the full path for |dir_id|.
764 std::deque
<std::string
> dir_path_parts
;
765 MTPFileNode
* parent_node
= dir_node
;
766 while (parent_node
->parent()) {
767 dir_path_parts
.push_front(parent_node
->file_name());
768 parent_node
= parent_node
->parent();
770 base::FilePath dir_path
= device_path_
;
771 for (size_t i
= 0; i
< dir_path_parts
.size(); ++i
)
772 dir_path
= dir_path
.Append(dir_path_parts
[i
]);
774 storage::AsyncFileUtil::EntryList normalized_file_list
;
775 for (size_t i
= 0; i
< file_list
.size(); ++i
) {
776 normalized_file_list
.push_back(file_list
[i
]);
777 storage::DirectoryEntry
& entry
= normalized_file_list
.back();
779 // |entry.name| has the file id encoded in it. Decode here.
780 size_t separator_idx
= entry
.name
.find_last_of(',');
781 DCHECK_NE(std::string::npos
, separator_idx
);
782 std::string file_id_str
= entry
.name
.substr(separator_idx
);
783 file_id_str
= file_id_str
.substr(1); // Get rid of the comma.
785 bool ret
= base::StringToUint(file_id_str
, &file_id
);
787 entry
.name
= entry
.name
.substr(0, separator_idx
);
789 // Refresh the in memory tree.
790 dir_node
->EnsureChildExists(entry
.name
, file_id
);
791 child_nodes_seen_
.insert(entry
.name
);
793 // Add to |file_info_cache_|.
794 file_info_cache_
[dir_path
.Append(entry
.name
)] = entry
;
797 success_callback
.Run(normalized_file_list
, has_more
);
799 return; // Wait to be called again.
801 // Last call, finish book keeping and continue with the next request.
802 dir_node
->ClearNonexistentChildren(child_nodes_seen_
);
803 child_nodes_seen_
.clear();
804 file_info_cache_
.clear();
806 PendingRequestDone();
809 void MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile(
810 const base::File::Info
& file_info
,
811 const base::FilePath
& snapshot_file_path
) {
812 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
813 DCHECK(current_snapshot_request_info_
.get());
814 current_snapshot_request_info_
->success_callback
.Run(
815 file_info
, snapshot_file_path
);
816 current_snapshot_request_info_
.reset();
817 PendingRequestDone();
820 void MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError(
821 base::File::Error error
) {
822 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
823 DCHECK(current_snapshot_request_info_
.get());
824 current_snapshot_request_info_
->error_callback
.Run(error
);
825 current_snapshot_request_info_
.reset();
826 PendingRequestDone();
829 void MTPDeviceDelegateImplLinux::OnDidReadBytes(
830 const ReadBytesSuccessCallback
& success_callback
,
831 const base::File::Info
& file_info
, int bytes_read
) {
832 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
833 success_callback
.Run(file_info
, bytes_read
);
834 PendingRequestDone();
837 void MTPDeviceDelegateImplLinux::OnDidFillFileCache(
838 const base::FilePath
& path
,
839 const storage::AsyncFileUtil::EntryList
& /* file_list */,
841 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
842 DCHECK(path
.IsParent(pending_tasks_
.front().path
));
844 return; // Wait until all entries have been read.
845 pending_tasks_
.front().cached_path
= path
;
848 void MTPDeviceDelegateImplLinux::OnFillFileCacheFailed(
849 base::File::Error
/* error */) {
850 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
851 // When filling the cache fails for the task at the front of the queue, clear
852 // the path of the task so it will not try to do any more caching. Instead,
853 // the task will just run and fail the CachedPathToId() lookup.
854 pending_tasks_
.front().path
.clear();
857 void MTPDeviceDelegateImplLinux::HandleDeviceFileError(
858 const ErrorCallback
& error_callback
,
860 base::File::Error error
) {
861 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
863 FileIdToMTPFileNodeMap::iterator it
= file_id_to_node_map_
.find(file_id
);
864 if (it
!= file_id_to_node_map_
.end()) {
865 MTPFileNode
* parent
= it
->second
->parent();
867 bool ret
= parent
->DeleteChild(file_id
);
871 error_callback
.Run(error
);
872 PendingRequestDone();
875 base::FilePath
MTPDeviceDelegateImplLinux::NextUncachedPathComponent(
876 const base::FilePath
& path
,
877 const base::FilePath
& cached_path
) const {
878 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
879 DCHECK(cached_path
.empty() || cached_path
.IsParent(path
));
881 base::FilePath uncached_path
;
882 std::string device_relpath
= GetDeviceRelativePath(device_path_
, path
);
883 if (!device_relpath
.empty() && device_relpath
!= kRootPath
) {
884 uncached_path
= device_path_
;
885 std::vector
<std::string
> device_relpath_components
;
886 base::SplitString(device_relpath
, '/', &device_relpath_components
);
887 DCHECK(!device_relpath_components
.empty());
888 bool all_components_cached
= true;
889 const MTPFileNode
* current_node
= root_node_
.get();
890 for (size_t i
= 0; i
< device_relpath_components
.size(); ++i
) {
891 current_node
= current_node
->GetChild(device_relpath_components
[i
]);
893 // With a cache miss, check if it is a genuine failure. If so, pretend
894 // the entire |path| is cached, so there is no further attempt to do
895 // more caching. The actual operation will then fail.
896 all_components_cached
=
897 !cached_path
.empty() && (uncached_path
== cached_path
);
900 uncached_path
= uncached_path
.Append(device_relpath_components
[i
]);
902 if (all_components_cached
)
903 uncached_path
.clear();
905 return uncached_path
;
908 void MTPDeviceDelegateImplLinux::FillFileCache(
909 const base::FilePath
& uncached_path
) {
910 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
911 DCHECK(task_in_progress_
);
913 ReadDirectorySuccessCallback success_callback
=
914 base::Bind(&MTPDeviceDelegateImplLinux::OnDidFillFileCache
,
915 weak_ptr_factory_
.GetWeakPtr(),
917 ErrorCallback error_callback
=
918 base::Bind(&MTPDeviceDelegateImplLinux::OnFillFileCacheFailed
,
919 weak_ptr_factory_
.GetWeakPtr());
920 ReadDirectoryInternal(uncached_path
, success_callback
, error_callback
);
924 bool MTPDeviceDelegateImplLinux::CachedPathToId(const base::FilePath
& path
,
928 std::string device_relpath
= GetDeviceRelativePath(device_path_
, path
);
929 if (device_relpath
.empty())
931 std::vector
<std::string
> device_relpath_components
;
932 if (device_relpath
!= kRootPath
)
933 base::SplitString(device_relpath
, '/', &device_relpath_components
);
934 const MTPFileNode
* current_node
= root_node_
.get();
935 for (size_t i
= 0; i
< device_relpath_components
.size(); ++i
) {
936 current_node
= current_node
->GetChild(device_relpath_components
[i
]);
940 *id
= current_node
->file_id();
944 void CreateMTPDeviceAsyncDelegate(
945 const std::string
& device_location
,
946 const CreateMTPDeviceAsyncDelegateCallback
& callback
) {
947 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
948 callback
.Run(new MTPDeviceDelegateImplLinux(device_location
));