Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / media_galleries / linux / mtp_device_delegate_impl_linux.cc
blob424365a08fdf1dcd0c6db7ca4893b2911b7c78ae
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.h"
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"
15 namespace {
17 // File path separator constant.
18 const char kRootPath[] = "/";
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;
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();
39 // Returns the MTPDeviceTaskHelper object associated with the MTP device
40 // storage.
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);
52 // Opens the storage device for communication.
54 // Called on the UI thread to dispatch the request to the
55 // MediaTransferProtocolManager.
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);
71 task_helper->OpenStorage(storage_name, reply_callback);
74 // Enumerates the |root| directory file entries.
76 // Called on the UI thread to dispatch the request to the
77 // MediaTransferProtocolManager.
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);
97 // Gets the |file_path| details.
99 // Called on the UI thread to dispatch the request to the
100 // MediaTransferProtocolManager.
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);
119 // Copies the contents of |device_file_path| to |snapshot_file_path|.
121 // Called on the UI thread to dispatch the request to the
122 // MediaTransferProtocolManager.
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);
144 // Closes the device storage specified by the |storage_name| and destroys the
145 // MTPDeviceTaskHelper object associated with the device storage.
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);
161 } // namespace
163 MTPDeviceDelegateImplLinux::PendingTaskInfo::PendingTaskInfo(
164 const tracked_objects::Location& location,
165 const base::Closure& task)
166 : location(location),
167 task(task) {
170 MTPDeviceDelegateImplLinux::PendingTaskInfo::~PendingTaskInfo() {
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 base::RemoveChars(device_location, kRootPath, &storage_name_);
182 DCHECK(!storage_name_.empty());
185 MTPDeviceDelegateImplLinux::~MTPDeviceDelegateImplLinux() {
186 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
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));
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));
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));
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;
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;
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())));
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()));
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);
322 void MTPDeviceDelegateImplLinux::ProcessNextPendingRequest() {
323 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
324 DCHECK(!task_in_progress_);
325 if (pending_tasks_.empty())
326 return;
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();
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();
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();
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);
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);
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;
392 if (error != base::PLATFORM_FILE_OK)
393 return HandleDeviceFileError(snapshot_request_info->error_callback, error);
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();
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);
406 WriteDataIntoSnapshotFile(snapshot_file_info);
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();
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();
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();
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();
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));