Upstream version 10.39.225.0
[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               const std::string& file_name,
211               MTPFileNode* parent,
212               FileIdToMTPFileNodeMap* file_id_to_node_map);
213   ~MTPFileNode();
214
215   const MTPFileNode* GetChild(const std::string& name) const;
216
217   void EnsureChildExists(const std::string& name, uint32 id);
218
219   // Clears all the children, except those in |children_to_keep|.
220   void ClearNonexistentChildren(
221       const std::set<std::string>& children_to_keep);
222
223   bool DeleteChild(uint32 file_id);
224
225   uint32 file_id() const { return file_id_; }
226   const std::string& file_name() const { return file_name_; }
227   MTPFileNode* parent() { return parent_; }
228
229  private:
230   // Container for holding a node's children.
231   typedef base::ScopedPtrHashMap<std::string, MTPFileNode> ChildNodes;
232
233   const uint32 file_id_;
234   const std::string file_name_;
235
236   ChildNodes children_;
237   MTPFileNode* const parent_;
238   FileIdToMTPFileNodeMap* file_id_to_node_map_;
239
240   DISALLOW_COPY_AND_ASSIGN(MTPFileNode);
241 };
242
243 MTPDeviceDelegateImplLinux::MTPFileNode::MTPFileNode(
244     uint32 file_id,
245     const std::string& file_name,
246     MTPFileNode* parent,
247     FileIdToMTPFileNodeMap* file_id_to_node_map)
248     : file_id_(file_id),
249       file_name_(file_name),
250       parent_(parent),
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;
256 }
257
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);
262 }
263
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);
269 }
270
271 void MTPDeviceDelegateImplLinux::MTPFileNode::EnsureChildExists(
272     const std::string& name,
273     uint32 id) {
274   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
275   const MTPFileNode* child = GetChild(name);
276   if (child && child->file_id() == id)
277     return;
278
279   children_.set(
280       name,
281       make_scoped_ptr(new MTPFileNode(id, name, this, file_id_to_node_map_)));
282 }
283
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))
291       continue;
292     children_to_erase.insert(it->first);
293   }
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);
297   }
298 }
299
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) {
305       children_.erase(it);
306       return true;
307     }
308   }
309   return false;
310 }
311
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());
326 }
327
328 MTPDeviceDelegateImplLinux::~MTPDeviceDelegateImplLinux() {
329   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
330 }
331
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());
338
339   // If a ReadDirectory operation is in progress, the file info may already be
340   // cached.
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();
352
353     success_callback.Run(info);
354     return;
355   }
356   base::Closure closure =
357       base::Bind(&MTPDeviceDelegateImplLinux::GetFileInfoInternal,
358                  weak_ptr_factory_.GetWeakPtr(),
359                  file_path,
360                  success_callback,
361                  error_callback);
362   EnsureInitAndRunTask(PendingTaskInfo(file_path,
363                                        content::BrowserThread::IO,
364                                        FROM_HERE,
365                                        closure));
366 }
367
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(),
377                  root,
378                  success_callback,
379                  error_callback);
380   EnsureInitAndRunTask(PendingTaskInfo(root,
381                                        content::BrowserThread::IO,
382                                        FROM_HERE,
383                                        closure));
384 }
385
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(),
397                  device_file_path,
398                  local_path,
399                  success_callback,
400                  error_callback);
401   EnsureInitAndRunTask(PendingTaskInfo(device_file_path,
402                                        content::BrowserThread::IO,
403                                        FROM_HERE,
404                                        closure));
405 }
406
407 bool MTPDeviceDelegateImplLinux::IsStreaming() {
408   return true;
409 }
410
411 void MTPDeviceDelegateImplLinux::ReadBytes(
412     const base::FilePath& device_file_path,
413     const scoped_refptr<net::IOBuffer>& buf,
414     int64 offset,
415     int buf_len,
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(),
423                  device_file_path,
424                  buf,
425                  offset,
426                  buf_len,
427                  success_callback,
428                  error_callback);
429   EnsureInitAndRunTask(PendingTaskInfo(device_file_path,
430                                        content::BrowserThread::IO,
431                                        FROM_HERE,
432                                        closure));
433 }
434
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,
440       FROM_HERE,
441       base::Bind(&CloseStorageAndDestroyTaskHelperOnUIThread, storage_name_));
442   delete this;
443 }
444
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);
450
451   uint32 file_id;
452   if (CachedPathToId(file_path, &file_id)) {
453     GetFileInfoSuccessCallback success_callback_wrapper =
454         base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfo,
455                    weak_ptr_factory_.GetWeakPtr(),
456                    success_callback);
457     ErrorCallback error_callback_wrapper =
458         base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
459                    weak_ptr_factory_.GetWeakPtr(),
460                    error_callback,
461                    file_id);
462
463
464     base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
465                                        storage_name_,
466                                        file_id,
467                                        success_callback_wrapper,
468                                        error_callback_wrapper);
469     EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
470                                          content::BrowserThread::UI,
471                                          FROM_HERE,
472                                          closure));
473   } else {
474     error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
475   }
476   PendingRequestDone();
477 }
478
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);
484
485   uint32 dir_id;
486   if (CachedPathToId(root, &dir_id)) {
487     GetFileInfoSuccessCallback success_callback_wrapper =
488         base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory,
489                    weak_ptr_factory_.GetWeakPtr(),
490                    dir_id,
491                    success_callback,
492                    error_callback);
493     ErrorCallback error_callback_wrapper =
494         base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
495                    weak_ptr_factory_.GetWeakPtr(),
496                    error_callback,
497                    dir_id);
498     base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
499                                        storage_name_,
500                                        dir_id,
501                                        success_callback_wrapper,
502                                        error_callback_wrapper);
503     EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
504                                          content::BrowserThread::UI,
505                                          FROM_HERE,
506                                          closure));
507   } else {
508     error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
509   }
510   PendingRequestDone();
511 }
512
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);
519
520   uint32 file_id;
521   if (CachedPathToId(device_file_path, &file_id)) {
522     scoped_ptr<SnapshotRequestInfo> request_info(
523         new SnapshotRequestInfo(file_id,
524                                 local_path,
525                                 success_callback,
526                                 error_callback));
527     GetFileInfoSuccessCallback success_callback_wrapper =
528         base::Bind(
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(),
535                    error_callback,
536                    file_id);
537     base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
538                                        storage_name_,
539                                        file_id,
540                                        success_callback_wrapper,
541                                        error_callback_wrapper);
542     EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
543                                          content::BrowserThread::UI,
544                                          FROM_HERE,
545                                          closure));
546   } else {
547     error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
548   }
549   PendingRequestDone();
550 }
551
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);
558
559   uint32 file_id;
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(),
565                    success_callback),
566         base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
567                    weak_ptr_factory_.GetWeakPtr(),
568                    error_callback,
569                    file_id));
570
571     base::Closure closure =
572         base::Bind(base::Bind(&ReadBytesOnUIThread, storage_name_, request));
573     EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
574                                          content::BrowserThread::UI,
575                                          FROM_HERE,
576                                          closure));
577   } else {
578     error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
579   }
580   PendingRequestDone();
581 }
582
583 void MTPDeviceDelegateImplLinux::EnsureInitAndRunTask(
584     const PendingTaskInfo& task_info) {
585   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
586   if ((init_state_ == INITIALIZED) && !task_in_progress_) {
587     RunTask(task_info);
588     return;
589   }
590
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);
595   else
596     pending_tasks_.push_back(task_info);
597
598   if (init_state_ == UNINITIALIZED) {
599     init_state_ = PENDING_INIT;
600     task_in_progress_ = true;
601     content::BrowserThread::PostTask(
602         content::BrowserThread::UI,
603         FROM_HERE,
604         base::Bind(&OpenStorageOnUIThread,
605                    storage_name_,
606                    base::Bind(&MTPDeviceDelegateImplLinux::OnInitCompleted,
607                               weak_ptr_factory_.GetWeakPtr())));
608   }
609 }
610
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;
616
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);
625       return;
626     }
627   }
628
629   content::BrowserThread::PostTask(task_info.thread_id,
630                           task_info.location,
631                           task_info.task);
632 }
633
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,
643       base::Bind(
644           &MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile,
645           weak_ptr_factory_.GetWeakPtr()),
646       base::Bind(
647           &MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError,
648           weak_ptr_factory_.GetWeakPtr()));
649
650   base::Closure task_closure = base::Bind(&WriteDataIntoSnapshotFileOnUIThread,
651                                           storage_name_,
652                                           request_info,
653                                           file_info);
654   content::BrowserThread::PostTask(content::BrowserThread::UI,
655                                    FROM_HERE,
656                                    task_closure);
657 }
658
659 void MTPDeviceDelegateImplLinux::PendingRequestDone() {
660   DCHECK(task_in_progress_);
661   task_in_progress_ = false;
662   ProcessNextPendingRequest();
663 }
664
665 void MTPDeviceDelegateImplLinux::ProcessNextPendingRequest() {
666   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
667   DCHECK(!task_in_progress_);
668   if (pending_tasks_.empty())
669     return;
670
671   PendingTaskInfo task_info = pending_tasks_.front();
672   pending_tasks_.pop_front();
673   RunTask(task_info);
674 }
675
676 void MTPDeviceDelegateImplLinux::OnInitCompleted(bool succeeded) {
677   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
678   init_state_ = succeeded ? INITIALIZED : UNINITIALIZED;
679   PendingRequestDone();
680 }
681
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();
688 }
689
690 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory(
691     uint32 dir_id,
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,
699                                  dir_id,
700                                  base::File::FILE_ERROR_NOT_A_DIRECTORY);
701   }
702
703   base::Closure task_closure =
704       base::Bind(&ReadDirectoryOnUIThread,
705                  storage_name_,
706                  dir_id,
707                  base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadDirectory,
708                             weak_ptr_factory_.GetWeakPtr(),
709                             dir_id,
710                             success_callback),
711                  base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
712                             weak_ptr_factory_.GetWeakPtr(),
713                             error_callback,
714                             dir_id));
715   content::BrowserThread::PostTask(content::BrowserThread::UI,
716                                    FROM_HERE,
717                                    task_closure);
718 }
719
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;
732
733   if (error != base::File::FILE_OK)
734     return HandleDeviceFileError(snapshot_request_info->error_callback,
735                                  snapshot_request_info->file_id,
736                                  error);
737
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();
742
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);
748   }
749   WriteDataIntoSnapshotFile(snapshot_file_info);
750 }
751
752 void MTPDeviceDelegateImplLinux::OnDidReadDirectory(
753     uint32 dir_id,
754     const ReadDirectorySuccessCallback& success_callback,
755     const storage::AsyncFileUtil::EntryList& file_list,
756     bool has_more) {
757   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
758
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;
762
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();
769   }
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]);
773
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();
778
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.
784     uint32 file_id = 0;
785     bool ret = base::StringToUint(file_id_str, &file_id);
786     DCHECK(ret);
787     entry.name = entry.name.substr(0, separator_idx);
788
789     // Refresh the in memory tree.
790     dir_node->EnsureChildExists(entry.name, file_id);
791     child_nodes_seen_.insert(entry.name);
792
793     // Add to |file_info_cache_|.
794     file_info_cache_[dir_path.Append(entry.name)] = entry;
795   }
796
797   success_callback.Run(normalized_file_list, has_more);
798   if (has_more)
799     return;  // Wait to be called again.
800
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();
805
806   PendingRequestDone();
807 }
808
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();
818 }
819
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();
827 }
828
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();
835 }
836
837 void MTPDeviceDelegateImplLinux::OnDidFillFileCache(
838     const base::FilePath& path,
839     const storage::AsyncFileUtil::EntryList& /* file_list */,
840     bool has_more) {
841   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
842   DCHECK(path.IsParent(pending_tasks_.front().path));
843   if (has_more)
844     return;  // Wait until all entries have been read.
845   pending_tasks_.front().cached_path = path;
846 }
847
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();
855 }
856
857 void MTPDeviceDelegateImplLinux::HandleDeviceFileError(
858     const ErrorCallback& error_callback,
859     uint32 file_id,
860     base::File::Error error) {
861   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
862
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();
866     if (parent) {
867       bool ret = parent->DeleteChild(file_id);
868       DCHECK(ret);
869     }
870   }
871   error_callback.Run(error);
872   PendingRequestDone();
873 }
874
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));
880
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]);
892       if (!current_node) {
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);
898         break;
899       }
900       uncached_path = uncached_path.Append(device_relpath_components[i]);
901     }
902     if (all_components_cached)
903       uncached_path.clear();
904   }
905   return uncached_path;
906 }
907
908 void MTPDeviceDelegateImplLinux::FillFileCache(
909     const base::FilePath& uncached_path) {
910   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
911   DCHECK(task_in_progress_);
912
913   ReadDirectorySuccessCallback success_callback =
914       base::Bind(&MTPDeviceDelegateImplLinux::OnDidFillFileCache,
915                  weak_ptr_factory_.GetWeakPtr(),
916                  uncached_path);
917   ErrorCallback error_callback =
918       base::Bind(&MTPDeviceDelegateImplLinux::OnFillFileCacheFailed,
919                  weak_ptr_factory_.GetWeakPtr());
920   ReadDirectoryInternal(uncached_path, success_callback, error_callback);
921 }
922
923
924 bool MTPDeviceDelegateImplLinux::CachedPathToId(const base::FilePath& path,
925                                                 uint32* id) const {
926   DCHECK(id);
927
928   std::string device_relpath = GetDeviceRelativePath(device_path_, path);
929   if (device_relpath.empty())
930     return false;
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]);
937     if (!current_node)
938       return false;
939   }
940   *id = current_node->file_id();
941   return true;
942 }
943
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));
949 }