82bf26ce53bb952fc88eb276b1e4c6eaeefdb1d8
[platform/framework/web/crosswalk.git] / src / chrome / browser / media_galleries / linux / mtp_device_delegate_impl_linux.cc
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.
4
5 #include "chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.h"
6
7 #include <algorithm>
8 #include <vector>
9
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"
20
21 namespace {
22
23 // File path separator constant.
24 const char kRootPath[] = "/";
25
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
29 // "DCIM".
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());
37   std::string result;
38   if (registered_dev_path == file_path) {
39     result = kRootPath;
40   } else {
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();
45     }
46   }
47   return result;
48 }
49
50 // Returns the MTPDeviceTaskHelper object associated with the MTP device
51 // storage.
52 //
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(
60       storage_name);
61 }
62
63 // Opens the storage device for communication.
64 //
65 // Called on the UI thread to dispatch the request to the
66 // MediaTransferProtocolManager.
67 //
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);
77   if (!task_helper) {
78     task_helper =
79         MTPDeviceTaskHelperMapService::GetInstance()->CreateDeviceTaskHelper(
80             storage_name);
81   }
82   task_helper->OpenStorage(storage_name, reply_callback);
83 }
84
85 // Enumerates the |dir_id| directory file entries.
86 //
87 // Called on the UI thread to dispatch the request to the
88 // MediaTransferProtocolManager.
89 //
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,
96     uint32 dir_id,
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);
102   if (!task_helper)
103     return;
104   task_helper->ReadDirectory(dir_id, success_callback, error_callback);
105 }
106
107 // Gets the |file_path| details.
108 //
109 // Called on the UI thread to dispatch the request to the
110 // MediaTransferProtocolManager.
111 //
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,
118     uint32 file_id,
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);
124   if (!task_helper)
125     return;
126   task_helper->GetFileInfo(file_id, success_callback, error_callback);
127 }
128
129 // Copies the contents of |device_file_path| to |snapshot_file_path|.
130 //
131 // Called on the UI thread to dispatch the request to the
132 // MediaTransferProtocolManager.
133 //
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
138 // snapshot file.
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);
149   if (!task_helper)
150     return;
151   task_helper->WriteDataIntoSnapshotFile(request_info, snapshot_file_info);
152 }
153
154 // Copies the contents of |device_file_path| to |snapshot_file_path|.
155 //
156 // Called on the UI thread to dispatch the request to the
157 // MediaTransferProtocolManager.
158 //
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);
167   if (!task_helper)
168     return;
169   task_helper->ReadBytes(request);
170 }
171
172 // Closes the device storage specified by the |storage_name| and destroys the
173 // MTPDeviceTaskHelper object associated with the device storage.
174 //
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);
182   if (!task_helper)
183     return;
184   task_helper->CloseStorage();
185   MTPDeviceTaskHelperMapService::GetInstance()->DestroyDeviceTaskHelper(
186       storage_name);
187 }
188
189 }  // namespace
190
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)
196     : path(path),
197       thread_id(thread_id),
198       location(location),
199       task(task) {
200 }
201
202 MTPDeviceDelegateImplLinux::PendingTaskInfo::~PendingTaskInfo() {
203 }
204
205 // Represents a file on the MTP device.
206 // Lives on the IO thread.
207 class MTPDeviceDelegateImplLinux::MTPFileNode {
208  public:
209   MTPFileNode(uint32 file_id,
210               MTPFileNode* parent,
211               FileIdToMTPFileNodeMap* file_id_to_node_map);
212   ~MTPFileNode();
213
214   const MTPFileNode* GetChild(const std::string& name) const;
215
216   void EnsureChildExists(const std::string& name, uint32 id);
217
218   // Clears all the children, except those in |children_to_keep|.
219   void ClearNonexistentChildren(
220       const std::set<std::string>& children_to_keep);
221
222   bool DeleteChild(uint32 file_id);
223
224   uint32 file_id() const { return file_id_; }
225   MTPFileNode* parent() { return parent_; }
226
227  private:
228   // Container for holding a node's children.
229   typedef base::ScopedPtrHashMap<std::string, MTPFileNode> ChildNodes;
230
231   const uint32 file_id_;
232   ChildNodes children_;
233   MTPFileNode* const parent_;
234   FileIdToMTPFileNodeMap* file_id_to_node_map_;
235
236   DISALLOW_COPY_AND_ASSIGN(MTPFileNode);
237 };
238
239 MTPDeviceDelegateImplLinux::MTPFileNode::MTPFileNode(
240     uint32 file_id,
241     MTPFileNode* parent,
242     FileIdToMTPFileNodeMap* file_id_to_node_map)
243     : file_id_(file_id),
244       parent_(parent),
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;
250 }
251
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);
256 }
257
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);
263 }
264
265 void MTPDeviceDelegateImplLinux::MTPFileNode::EnsureChildExists(
266     const std::string& name,
267     uint32 id) {
268   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
269   const MTPFileNode* child = GetChild(name);
270   if (child && child->file_id() == id)
271     return;
272
273   children_.set(
274       name,
275       make_scoped_ptr(new MTPFileNode(id, this, file_id_to_node_map_)));
276 }
277
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))
285       continue;
286     children_to_erase.insert(it->first);
287   }
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);
291   }
292 }
293
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) {
299       children_.erase(it);
300       return true;
301     }
302   }
303   return false;
304 }
305
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,
312                                  NULL,
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());
319 }
320
321 MTPDeviceDelegateImplLinux::~MTPDeviceDelegateImplLinux() {
322   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
323 }
324
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(),
334                  file_path,
335                  success_callback,
336                  error_callback);
337   EnsureInitAndRunTask(PendingTaskInfo(file_path,
338                                        content::BrowserThread::IO,
339                                        FROM_HERE,
340                                        closure));
341 }
342
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(),
352                  root,
353                  success_callback,
354                  error_callback);
355   EnsureInitAndRunTask(PendingTaskInfo(root,
356                                        content::BrowserThread::IO,
357                                        FROM_HERE,
358                                        closure));
359 }
360
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(),
372                  device_file_path,
373                  local_path,
374                  success_callback,
375                  error_callback);
376   EnsureInitAndRunTask(PendingTaskInfo(device_file_path,
377                                        content::BrowserThread::IO,
378                                        FROM_HERE,
379                                        closure));
380 }
381
382 bool MTPDeviceDelegateImplLinux::IsStreaming() {
383   return true;
384 }
385
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(),
396                  device_file_path,
397                  make_scoped_refptr(buf),
398                  offset,
399                  buf_len,
400                  success_callback,
401                  error_callback);
402   EnsureInitAndRunTask(PendingTaskInfo(device_file_path,
403                                        content::BrowserThread::IO,
404                                        FROM_HERE,
405                                        closure));
406 }
407
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,
413       FROM_HERE,
414       base::Bind(&CloseStorageAndDestroyTaskHelperOnUIThread, storage_name_));
415   delete this;
416 }
417
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);
423
424   uint32 file_id;
425   if (CachedPathToId(file_path, &file_id)) {
426     GetFileInfoSuccessCallback success_callback_wrapper =
427         base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfo,
428                    weak_ptr_factory_.GetWeakPtr(),
429                    success_callback);
430     ErrorCallback error_callback_wrapper =
431         base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
432                    weak_ptr_factory_.GetWeakPtr(),
433                    error_callback,
434                    file_id);
435
436
437     base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
438                                        storage_name_,
439                                        file_id,
440                                        success_callback_wrapper,
441                                        error_callback_wrapper);
442     EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
443                                          content::BrowserThread::UI,
444                                          FROM_HERE,
445                                          closure));
446   } else {
447     error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
448   }
449   PendingRequestDone();
450 }
451
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);
457
458   uint32 dir_id;
459   if (CachedPathToId(root, &dir_id)) {
460     GetFileInfoSuccessCallback success_callback_wrapper =
461         base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory,
462                    weak_ptr_factory_.GetWeakPtr(),
463                    dir_id,
464                    success_callback,
465                    error_callback);
466     ErrorCallback error_callback_wrapper =
467         base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
468                    weak_ptr_factory_.GetWeakPtr(),
469                    error_callback,
470                    dir_id);
471     base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
472                                        storage_name_,
473                                        dir_id,
474                                        success_callback_wrapper,
475                                        error_callback_wrapper);
476     EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
477                                          content::BrowserThread::UI,
478                                          FROM_HERE,
479                                          closure));
480   } else {
481     error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
482   }
483   PendingRequestDone();
484 }
485
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);
492
493   uint32 file_id;
494   if (CachedPathToId(device_file_path, &file_id)) {
495     scoped_ptr<SnapshotRequestInfo> request_info(
496         new SnapshotRequestInfo(file_id,
497                                 local_path,
498                                 success_callback,
499                                 error_callback));
500     GetFileInfoSuccessCallback success_callback_wrapper =
501         base::Bind(
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(),
508                    error_callback,
509                    file_id);
510     base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
511                                        storage_name_,
512                                        file_id,
513                                        success_callback_wrapper,
514                                        error_callback_wrapper);
515     EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
516                                          content::BrowserThread::UI,
517                                          FROM_HERE,
518                                          closure));
519   } else {
520     error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
521   }
522   PendingRequestDone();
523 }
524
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);
531
532   uint32 file_id;
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(),
538                    success_callback),
539         base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
540                    weak_ptr_factory_.GetWeakPtr(),
541                    error_callback,
542                    file_id));
543
544     base::Closure closure =
545         base::Bind(base::Bind(&ReadBytesOnUIThread, storage_name_, request));
546     EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
547                                          content::BrowserThread::UI,
548                                          FROM_HERE,
549                                          closure));
550   } else {
551     error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
552   }
553   PendingRequestDone();
554 }
555
556 void MTPDeviceDelegateImplLinux::EnsureInitAndRunTask(
557     const PendingTaskInfo& task_info) {
558   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
559   if ((init_state_ == INITIALIZED) && !task_in_progress_) {
560     RunTask(task_info);
561     return;
562   }
563
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);
568   else
569     pending_tasks_.push_back(task_info);
570
571   if (init_state_ == UNINITIALIZED) {
572     init_state_ = PENDING_INIT;
573     task_in_progress_ = true;
574     content::BrowserThread::PostTask(
575         content::BrowserThread::UI,
576         FROM_HERE,
577         base::Bind(&OpenStorageOnUIThread,
578                    storage_name_,
579                    base::Bind(&MTPDeviceDelegateImplLinux::OnInitCompleted,
580                               weak_ptr_factory_.GetWeakPtr())));
581   }
582 }
583
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;
589
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);
598       return;
599     }
600   }
601
602   content::BrowserThread::PostTask(task_info.thread_id,
603                           task_info.location,
604                           task_info.task);
605 }
606
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,
616       base::Bind(
617           &MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile,
618           weak_ptr_factory_.GetWeakPtr()),
619       base::Bind(
620           &MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError,
621           weak_ptr_factory_.GetWeakPtr()));
622
623   base::Closure task_closure = base::Bind(&WriteDataIntoSnapshotFileOnUIThread,
624                                           storage_name_,
625                                           request_info,
626                                           file_info);
627   content::BrowserThread::PostTask(content::BrowserThread::UI,
628                                    FROM_HERE,
629                                    task_closure);
630 }
631
632 void MTPDeviceDelegateImplLinux::PendingRequestDone() {
633   DCHECK(task_in_progress_);
634   task_in_progress_ = false;
635   ProcessNextPendingRequest();
636 }
637
638 void MTPDeviceDelegateImplLinux::ProcessNextPendingRequest() {
639   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
640   DCHECK(!task_in_progress_);
641   if (pending_tasks_.empty())
642     return;
643
644   PendingTaskInfo task_info = pending_tasks_.front();
645   pending_tasks_.pop_front();
646   RunTask(task_info);
647 }
648
649 void MTPDeviceDelegateImplLinux::OnInitCompleted(bool succeeded) {
650   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
651   init_state_ = succeeded ? INITIALIZED : UNINITIALIZED;
652   PendingRequestDone();
653 }
654
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();
661 }
662
663 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory(
664     uint32 dir_id,
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,
672                                  dir_id,
673                                  base::File::FILE_ERROR_NOT_A_DIRECTORY);
674   }
675
676   base::Closure task_closure =
677       base::Bind(&ReadDirectoryOnUIThread,
678                  storage_name_,
679                  dir_id,
680                  base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadDirectory,
681                             weak_ptr_factory_.GetWeakPtr(),
682                             dir_id,
683                             success_callback),
684                  base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
685                             weak_ptr_factory_.GetWeakPtr(),
686                             error_callback,
687                             dir_id));
688   content::BrowserThread::PostTask(content::BrowserThread::UI,
689                                    FROM_HERE,
690                                    task_closure);
691 }
692
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;
705
706   if (error != base::File::FILE_OK)
707     return HandleDeviceFileError(snapshot_request_info->error_callback,
708                                  snapshot_request_info->file_id,
709                                  error);
710
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();
715
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);
721   }
722   WriteDataIntoSnapshotFile(snapshot_file_info);
723 }
724
725 void MTPDeviceDelegateImplLinux::OnDidReadDirectory(
726     uint32 dir_id,
727     const ReadDirectorySuccessCallback& success_callback,
728     const fileapi::AsyncFileUtil::EntryList& file_list,
729     bool has_more) {
730   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
731
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;
735
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();
740
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.
746     uint32 file_id = 0;
747     bool ret = base::StringToUint(file_id_str, &file_id);
748     DCHECK(ret);
749     entry.name = entry.name.substr(0, separator_idx);
750
751     // Refresh the in memory tree.
752     dir_node->EnsureChildExists(entry.name, file_id);
753     child_nodes_seen_.insert(entry.name);
754   }
755
756   success_callback.Run(normalized_file_list, has_more);
757   if (has_more)
758     return;  // Wait to be called again.
759
760   // Last call, finish book keeping and continue with the next request.
761   dir_node->ClearNonexistentChildren(child_nodes_seen_);
762   child_nodes_seen_.clear();
763
764   PendingRequestDone();
765 }
766
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();
776 }
777
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();
785 }
786
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();
793 }
794
795 void MTPDeviceDelegateImplLinux::OnDidFillFileCache(
796     const base::FilePath& path,
797     const fileapi::AsyncFileUtil::EntryList& /* file_list */,
798     bool has_more) {
799   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
800   DCHECK(path.IsParent(pending_tasks_.front().path));
801   if (has_more)
802     return;  // Wait until all entries have been read.
803   pending_tasks_.front().cached_path = path;
804 }
805
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();
813 }
814
815 void MTPDeviceDelegateImplLinux::HandleDeviceFileError(
816     const ErrorCallback& error_callback,
817     uint32 file_id,
818     base::File::Error error) {
819   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
820
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();
824     if (parent) {
825       bool ret = parent->DeleteChild(file_id);
826       DCHECK(ret);
827     }
828   }
829   error_callback.Run(error);
830   PendingRequestDone();
831 }
832
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));
838
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]);
850       if (!current_node) {
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);
856         break;
857       }
858       uncached_path = uncached_path.Append(device_relpath_components[i]);
859     }
860     if (all_components_cached)
861       uncached_path.clear();
862   }
863   return uncached_path;
864 }
865
866 void MTPDeviceDelegateImplLinux::FillFileCache(
867     const base::FilePath& uncached_path) {
868   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
869   DCHECK(task_in_progress_);
870
871   ReadDirectorySuccessCallback success_callback =
872       base::Bind(&MTPDeviceDelegateImplLinux::OnDidFillFileCache,
873                  weak_ptr_factory_.GetWeakPtr(),
874                  uncached_path);
875   ErrorCallback error_callback =
876       base::Bind(&MTPDeviceDelegateImplLinux::OnFillFileCacheFailed,
877                  weak_ptr_factory_.GetWeakPtr());
878   ReadDirectoryInternal(uncached_path, success_callback, error_callback);
879 }
880
881
882 bool MTPDeviceDelegateImplLinux::CachedPathToId(const base::FilePath& path,
883                                                 uint32* id) const {
884   DCHECK(id);
885
886   std::string device_relpath = GetDeviceRelativePath(device_path_, path);
887   if (device_relpath.empty())
888     return false;
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]);
895     if (!current_node)
896       return false;
897   }
898   *id = current_node->file_id();
899   return true;
900 }
901
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));
907 }