- add sources.
[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 "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "base/strings/string_util.h"
10 #include "chrome/browser/media_galleries/linux/mtp_device_task_helper.h"
11 #include "chrome/browser/media_galleries/linux/mtp_device_task_helper_map_service.h"
12 #include "chrome/browser/media_galleries/linux/snapshot_file_details.h"
13 #include "content/public/browser/browser_thread.h"
14
15 namespace {
16
17 // File path separator constant.
18 const char kRootPath[] = "/";
19
20 // Returns the device relative file path given |file_path|.
21 // E.g.: If the |file_path| is "/usb:2,2:12345/DCIM" and |registered_dev_path|
22 // is "/usb:2,2:12345", this function returns the device relative path which is
23 // "/DCIM".
24 std::string GetDeviceRelativePath(const base::FilePath& registered_dev_path,
25                                   const base::FilePath& file_path) {
26   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
27   DCHECK(!registered_dev_path.empty());
28   DCHECK(!file_path.empty());
29   if (registered_dev_path == file_path)
30     return kRootPath;
31
32   base::FilePath relative_path;
33   if (!registered_dev_path.AppendRelativePath(file_path, &relative_path))
34     return std::string();
35   DCHECK(!relative_path.empty());
36   return relative_path.value();
37 }
38
39 // Returns the MTPDeviceTaskHelper object associated with the MTP device
40 // storage.
41 //
42 // |storage_name| specifies the name of the storage device.
43 // Returns NULL if the |storage_name| is no longer valid (e.g. because the
44 // corresponding storage device is detached, etc).
45 MTPDeviceTaskHelper* GetDeviceTaskHelperForStorage(
46     const std::string& storage_name) {
47   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
48   return MTPDeviceTaskHelperMapService::GetInstance()->GetDeviceTaskHelper(
49       storage_name);
50 }
51
52 // Opens the storage device for communication.
53 //
54 // Called on the UI thread to dispatch the request to the
55 // MediaTransferProtocolManager.
56 //
57 // |storage_name| specifies the name of the storage device.
58 // |reply_callback| is called when the OpenStorage request completes.
59 // |reply_callback| runs on the IO thread.
60 void OpenStorageOnUIThread(
61     const std::string& storage_name,
62     const base::Callback<void(bool)>& reply_callback) {
63   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
64   MTPDeviceTaskHelper* task_helper =
65       GetDeviceTaskHelperForStorage(storage_name);
66   if (!task_helper) {
67     task_helper =
68         MTPDeviceTaskHelperMapService::GetInstance()->CreateDeviceTaskHelper(
69             storage_name);
70   }
71   task_helper->OpenStorage(storage_name, reply_callback);
72 }
73
74 // Enumerates the |root| directory file entries.
75 //
76 // Called on the UI thread to dispatch the request to the
77 // MediaTransferProtocolManager.
78 //
79 // |storage_name| specifies the name of the storage device.
80 // |success_callback| is called when the ReadDirectory request succeeds.
81 // |error_callback| is called when the ReadDirectory request fails.
82 // |success_callback| and |error_callback| runs on the IO thread.
83 void ReadDirectoryOnUIThread(
84     const std::string& storage_name,
85     const std::string& root,
86     const base::Callback<
87         void(const fileapi::AsyncFileUtil::EntryList&)>& success_callback,
88     const base::Callback<void(base::PlatformFileError)>& error_callback) {
89   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
90   MTPDeviceTaskHelper* task_helper =
91       GetDeviceTaskHelperForStorage(storage_name);
92   if (!task_helper)
93     return;
94   task_helper->ReadDirectoryByPath(root, success_callback, error_callback);
95 }
96
97 // Gets the |file_path| details.
98 //
99 // Called on the UI thread to dispatch the request to the
100 // MediaTransferProtocolManager.
101 //
102 // |storage_name| specifies the name of the storage device.
103 // |success_callback| is called when the GetFileInfo request succeeds.
104 // |error_callback| is called when the GetFileInfo request fails.
105 // |success_callback| and |error_callback| runs on the IO thread.
106 void GetFileInfoOnUIThread(
107     const std::string& storage_name,
108     const std::string& file_path,
109     const base::Callback<void(const base::PlatformFileInfo&)>& success_callback,
110     const base::Callback<void(base::PlatformFileError)>& error_callback) {
111   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
112   MTPDeviceTaskHelper* task_helper =
113       GetDeviceTaskHelperForStorage(storage_name);
114   if (!task_helper)
115     return;
116   task_helper->GetFileInfoByPath(file_path, success_callback, error_callback);
117 }
118
119 // Copies the contents of |device_file_path| to |snapshot_file_path|.
120 //
121 // Called on the UI thread to dispatch the request to the
122 // MediaTransferProtocolManager.
123 //
124 // |storage_name| specifies the name of the storage device.
125 // |device_file_path| specifies the media device file path.
126 // |snapshot_file_path| specifies the platform path of the snapshot file.
127 // |file_size| specifies the number of bytes that will be written to the
128 // snapshot file.
129 // |success_callback| is called when the copy operation succeeds.
130 // |error_callback| is called when the copy operation fails.
131 // |success_callback| and |error_callback| runs on the IO thread.
132 void WriteDataIntoSnapshotFileOnUIThread(
133     const std::string& storage_name,
134     const SnapshotRequestInfo& request_info,
135     const base::PlatformFileInfo& snapshot_file_info) {
136   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
137   MTPDeviceTaskHelper* task_helper =
138       GetDeviceTaskHelperForStorage(storage_name);
139   if (!task_helper)
140     return;
141   task_helper->WriteDataIntoSnapshotFile(request_info, snapshot_file_info);
142 }
143
144 // Closes the device storage specified by the |storage_name| and destroys the
145 // MTPDeviceTaskHelper object associated with the device storage.
146 //
147 // Called on the UI thread to dispatch the request to the
148 // MediaTransferProtocolManager.
149 void CloseStorageAndDestroyTaskHelperOnUIThread(
150     const std::string& storage_name) {
151   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
152   MTPDeviceTaskHelper* task_helper =
153       GetDeviceTaskHelperForStorage(storage_name);
154   if (!task_helper)
155     return;
156   task_helper->CloseStorage();
157   MTPDeviceTaskHelperMapService::GetInstance()->DestroyDeviceTaskHelper(
158       storage_name);
159 }
160
161 }  // namespace
162
163 MTPDeviceDelegateImplLinux::PendingTaskInfo::PendingTaskInfo(
164     const tracked_objects::Location& location,
165     const base::Closure& task)
166     : location(location),
167       task(task) {
168 }
169
170 MTPDeviceDelegateImplLinux::PendingTaskInfo::~PendingTaskInfo() {
171 }
172
173 MTPDeviceDelegateImplLinux::MTPDeviceDelegateImplLinux(
174     const std::string& device_location)
175     : init_state_(UNINITIALIZED),
176       task_in_progress_(false),
177       device_path_(device_location),
178       weak_ptr_factory_(this) {
179   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
180   DCHECK(!device_path_.empty());
181   RemoveChars(device_location, kRootPath, &storage_name_);
182   DCHECK(!storage_name_.empty());
183 }
184
185 MTPDeviceDelegateImplLinux::~MTPDeviceDelegateImplLinux() {
186   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
187 }
188
189 void MTPDeviceDelegateImplLinux::GetFileInfo(
190     const base::FilePath& file_path,
191     const GetFileInfoSuccessCallback& success_callback,
192     const ErrorCallback& error_callback) {
193   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
194   DCHECK(!file_path.empty());
195   base::Closure call_closure =
196       base::Bind(&GetFileInfoOnUIThread,
197                  storage_name_,
198                  GetDeviceRelativePath(device_path_, file_path),
199                  base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfo,
200                             weak_ptr_factory_.GetWeakPtr(),
201                             success_callback),
202                  base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
203                             weak_ptr_factory_.GetWeakPtr(),
204                             error_callback));
205   EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE, call_closure));
206 }
207
208 void MTPDeviceDelegateImplLinux::ReadDirectory(
209     const base::FilePath& root,
210     const ReadDirectorySuccessCallback& success_callback,
211     const ErrorCallback& error_callback) {
212   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
213   DCHECK(!root.empty());
214   std::string device_file_relative_path = GetDeviceRelativePath(device_path_,
215                                                                 root);
216   base::Closure call_closure =
217       base::Bind(
218           &GetFileInfoOnUIThread,
219           storage_name_,
220           device_file_relative_path,
221           base::Bind(
222               &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory,
223               weak_ptr_factory_.GetWeakPtr(),
224               device_file_relative_path,
225               success_callback,
226               error_callback),
227           base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
228                      weak_ptr_factory_.GetWeakPtr(),
229                      error_callback));
230   EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE, call_closure));
231 }
232
233 void MTPDeviceDelegateImplLinux::CreateSnapshotFile(
234     const base::FilePath& device_file_path,
235     const base::FilePath& snapshot_file_path,
236     const CreateSnapshotFileSuccessCallback& success_callback,
237     const ErrorCallback& error_callback) {
238   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
239   DCHECK(!device_file_path.empty());
240   DCHECK(!snapshot_file_path.empty());
241   std::string device_file_relative_path =
242       GetDeviceRelativePath(device_path_, device_file_path);
243   scoped_ptr<SnapshotRequestInfo> request_info(
244       new SnapshotRequestInfo(device_file_relative_path,
245                               snapshot_file_path,
246                               success_callback,
247                               error_callback));
248   base::Closure call_closure =
249       base::Bind(
250           &GetFileInfoOnUIThread,
251           storage_name_,
252           device_file_relative_path,
253           base::Bind(
254               &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile,
255               weak_ptr_factory_.GetWeakPtr(),
256               base::Passed(&request_info)),
257           base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
258                      weak_ptr_factory_.GetWeakPtr(),
259                      error_callback));
260   EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE, call_closure));
261 }
262
263 void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() {
264   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
265   // To cancel all the pending tasks, destroy the MTPDeviceTaskHelper object.
266   content::BrowserThread::PostTask(
267       content::BrowserThread::UI,
268       FROM_HERE,
269       base::Bind(&CloseStorageAndDestroyTaskHelperOnUIThread, storage_name_));
270   delete this;
271 }
272
273 void MTPDeviceDelegateImplLinux::EnsureInitAndRunTask(
274     const PendingTaskInfo& task_info) {
275   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
276   if ((init_state_ == INITIALIZED) && !task_in_progress_) {
277     task_in_progress_ = true;
278     content::BrowserThread::PostTask(content::BrowserThread::UI,
279                                      task_info.location,
280                                      task_info.task);
281     return;
282   }
283   pending_tasks_.push(task_info);
284   if (init_state_ == UNINITIALIZED) {
285     init_state_ = PENDING_INIT;
286     task_in_progress_ = true;
287     content::BrowserThread::PostTask(
288         content::BrowserThread::UI,
289         FROM_HERE,
290         base::Bind(&OpenStorageOnUIThread,
291                    storage_name_,
292                    base::Bind(&MTPDeviceDelegateImplLinux::OnInitCompleted,
293                               weak_ptr_factory_.GetWeakPtr())));
294   }
295 }
296
297 void MTPDeviceDelegateImplLinux::WriteDataIntoSnapshotFile(
298     const base::PlatformFileInfo& file_info) {
299   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
300   DCHECK(current_snapshot_request_info_.get());
301   DCHECK_GT(file_info.size, 0);
302   task_in_progress_ = true;
303   SnapshotRequestInfo request_info(
304       current_snapshot_request_info_->device_file_path,
305       current_snapshot_request_info_->snapshot_file_path,
306       base::Bind(
307           &MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile,
308           weak_ptr_factory_.GetWeakPtr()),
309       base::Bind(
310           &MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError,
311           weak_ptr_factory_.GetWeakPtr()));
312
313   base::Closure task_closure = base::Bind(&WriteDataIntoSnapshotFileOnUIThread,
314                                           storage_name_,
315                                           request_info,
316                                           file_info);
317   content::BrowserThread::PostTask(content::BrowserThread::UI,
318                                    FROM_HERE,
319                                    task_closure);
320 }
321
322 void MTPDeviceDelegateImplLinux::ProcessNextPendingRequest() {
323   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
324   DCHECK(!task_in_progress_);
325   if (pending_tasks_.empty())
326     return;
327
328   task_in_progress_ = true;
329   const PendingTaskInfo& task_info = pending_tasks_.front();
330   content::BrowserThread::PostTask(content::BrowserThread::UI,
331                                    task_info.location,
332                                    task_info.task);
333   pending_tasks_.pop();
334 }
335
336 void MTPDeviceDelegateImplLinux::OnInitCompleted(bool succeeded) {
337   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
338   init_state_ = succeeded ? INITIALIZED : UNINITIALIZED;
339   task_in_progress_ = false;
340   ProcessNextPendingRequest();
341 }
342
343 void MTPDeviceDelegateImplLinux::OnDidGetFileInfo(
344     const GetFileInfoSuccessCallback& success_callback,
345     const base::PlatformFileInfo& file_info) {
346   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
347   success_callback.Run(file_info);
348   task_in_progress_ = false;
349   ProcessNextPendingRequest();
350 }
351
352 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory(
353     const std::string& root,
354     const ReadDirectorySuccessCallback& success_callback,
355     const ErrorCallback& error_callback,
356     const base::PlatformFileInfo& file_info) {
357   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
358   DCHECK(task_in_progress_);
359   if (!file_info.is_directory) {
360     return HandleDeviceFileError(error_callback,
361                                  base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY);
362   }
363
364   base::Closure task_closure =
365       base::Bind(&ReadDirectoryOnUIThread,
366                  storage_name_,
367                  root,
368                  base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadDirectory,
369                             weak_ptr_factory_.GetWeakPtr(),
370                             success_callback),
371                  base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
372                             weak_ptr_factory_.GetWeakPtr(),
373                             error_callback));
374   content::BrowserThread::PostTask(content::BrowserThread::UI,
375                                    FROM_HERE,
376                                    task_closure);
377 }
378
379 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile(
380     scoped_ptr<SnapshotRequestInfo> snapshot_request_info,
381     const base::PlatformFileInfo& file_info) {
382   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
383   DCHECK(!current_snapshot_request_info_.get());
384   DCHECK(snapshot_request_info.get());
385   DCHECK(task_in_progress_);
386   base::PlatformFileError error = base::PLATFORM_FILE_OK;
387   if (file_info.is_directory)
388     error = base::PLATFORM_FILE_ERROR_NOT_A_FILE;
389   else if (file_info.size < 0 || file_info.size > kuint32max)
390     error = base::PLATFORM_FILE_ERROR_FAILED;
391
392   if (error != base::PLATFORM_FILE_OK)
393     return HandleDeviceFileError(snapshot_request_info->error_callback, error);
394
395   base::PlatformFileInfo snapshot_file_info(file_info);
396   // Modify the last modified time to null. This prevents the time stamp
397   // verfication in LocalFileStreamReader.
398   snapshot_file_info.last_modified = base::Time();
399
400   current_snapshot_request_info_.reset(snapshot_request_info.release());
401   if (file_info.size == 0) {
402     // Empty snapshot file.
403     return OnDidWriteDataIntoSnapshotFile(
404         snapshot_file_info, current_snapshot_request_info_->snapshot_file_path);
405   }
406   WriteDataIntoSnapshotFile(snapshot_file_info);
407 }
408
409 void MTPDeviceDelegateImplLinux::OnDidReadDirectory(
410     const ReadDirectorySuccessCallback& success_callback,
411     const fileapi::AsyncFileUtil::EntryList& file_list) {
412   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
413   success_callback.Run(file_list, false /*no more entries*/);
414   task_in_progress_ = false;
415   ProcessNextPendingRequest();
416 }
417
418 void MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile(
419     const base::PlatformFileInfo& file_info,
420     const base::FilePath& snapshot_file_path) {
421   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
422   DCHECK(current_snapshot_request_info_.get());
423   DCHECK(task_in_progress_);
424   current_snapshot_request_info_->success_callback.Run(
425       file_info, snapshot_file_path);
426   task_in_progress_ = false;
427   current_snapshot_request_info_.reset();
428   ProcessNextPendingRequest();
429 }
430
431 void MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError(
432     base::PlatformFileError error) {
433   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
434   DCHECK(current_snapshot_request_info_.get());
435   DCHECK(task_in_progress_);
436   current_snapshot_request_info_->error_callback.Run(error);
437   task_in_progress_ = false;
438   current_snapshot_request_info_.reset();
439   ProcessNextPendingRequest();
440 }
441
442 void MTPDeviceDelegateImplLinux::HandleDeviceFileError(
443     const ErrorCallback& error_callback,
444     base::PlatformFileError error) {
445   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
446   error_callback.Run(error);
447   task_in_progress_ = false;
448   ProcessNextPendingRequest();
449 }
450
451 void CreateMTPDeviceAsyncDelegate(
452     const std::string& device_location,
453     const CreateMTPDeviceAsyncDelegateCallback& callback) {
454   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
455   callback.Run(new MTPDeviceDelegateImplLinux(device_location));
456 }