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"
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 #include "net/base/io_buffer.h"
18 // File path separator constant.
19 const char kRootPath
[] = "/";
21 // Returns the device relative file path given |file_path|.
22 // E.g.: If the |file_path| is "/usb:2,2:12345/DCIM" and |registered_dev_path|
23 // is "/usb:2,2:12345", this function returns the device relative path which is
25 std::string
GetDeviceRelativePath(const base::FilePath
& registered_dev_path
,
26 const base::FilePath
& file_path
) {
27 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
28 DCHECK(!registered_dev_path
.empty());
29 DCHECK(!file_path
.empty());
30 if (registered_dev_path
== file_path
)
33 base::FilePath relative_path
;
34 if (!registered_dev_path
.AppendRelativePath(file_path
, &relative_path
))
36 DCHECK(!relative_path
.empty());
37 return relative_path
.value();
40 // Returns the MTPDeviceTaskHelper object associated with the MTP device
43 // |storage_name| specifies the name of the storage device.
44 // Returns NULL if the |storage_name| is no longer valid (e.g. because the
45 // corresponding storage device is detached, etc).
46 MTPDeviceTaskHelper
* GetDeviceTaskHelperForStorage(
47 const std::string
& storage_name
) {
48 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
49 return MTPDeviceTaskHelperMapService::GetInstance()->GetDeviceTaskHelper(
53 // Opens the storage device for communication.
55 // Called on the UI thread to dispatch the request to the
56 // MediaTransferProtocolManager.
58 // |storage_name| specifies the name of the storage device.
59 // |reply_callback| is called when the OpenStorage request completes.
60 // |reply_callback| runs on the IO thread.
61 void OpenStorageOnUIThread(
62 const std::string
& storage_name
,
63 const base::Callback
<void(bool)>& reply_callback
) {
64 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
65 MTPDeviceTaskHelper
* task_helper
=
66 GetDeviceTaskHelperForStorage(storage_name
);
69 MTPDeviceTaskHelperMapService::GetInstance()->CreateDeviceTaskHelper(
72 task_helper
->OpenStorage(storage_name
, reply_callback
);
75 // Enumerates the |root| directory file entries.
77 // Called on the UI thread to dispatch the request to the
78 // MediaTransferProtocolManager.
80 // |storage_name| specifies the name of the storage device.
81 // |success_callback| is called when the ReadDirectory request succeeds.
82 // |error_callback| is called when the ReadDirectory request fails.
83 // |success_callback| and |error_callback| runs on the IO thread.
84 void ReadDirectoryOnUIThread(
85 const std::string
& storage_name
,
86 const std::string
& root
,
88 void(const fileapi::AsyncFileUtil::EntryList
&)>& success_callback
,
89 const base::Callback
<void(base::File::Error
)>& error_callback
) {
90 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
91 MTPDeviceTaskHelper
* task_helper
=
92 GetDeviceTaskHelperForStorage(storage_name
);
95 task_helper
->ReadDirectoryByPath(root
, success_callback
, error_callback
);
98 // Gets the |file_path| details.
100 // Called on the UI thread to dispatch the request to the
101 // MediaTransferProtocolManager.
103 // |storage_name| specifies the name of the storage device.
104 // |success_callback| is called when the GetFileInfo request succeeds.
105 // |error_callback| is called when the GetFileInfo request fails.
106 // |success_callback| and |error_callback| runs on the IO thread.
107 void GetFileInfoOnUIThread(
108 const std::string
& storage_name
,
109 const std::string
& file_path
,
110 const base::Callback
<void(const base::File::Info
&)>& success_callback
,
111 const base::Callback
<void(base::File::Error
)>& error_callback
) {
112 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
113 MTPDeviceTaskHelper
* task_helper
=
114 GetDeviceTaskHelperForStorage(storage_name
);
117 task_helper
->GetFileInfoByPath(file_path
, success_callback
, error_callback
);
120 // Copies the contents of |device_file_path| to |snapshot_file_path|.
122 // Called on the UI thread to dispatch the request to the
123 // MediaTransferProtocolManager.
125 // |storage_name| specifies the name of the storage device.
126 // |device_file_path| specifies the media device file path.
127 // |snapshot_file_path| specifies the platform path of the snapshot file.
128 // |file_size| specifies the number of bytes that will be written to the
130 // |success_callback| is called when the copy operation succeeds.
131 // |error_callback| is called when the copy operation fails.
132 // |success_callback| and |error_callback| runs on the IO thread.
133 void WriteDataIntoSnapshotFileOnUIThread(
134 const std::string
& storage_name
,
135 const SnapshotRequestInfo
& request_info
,
136 const base::File::Info
& snapshot_file_info
) {
137 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
138 MTPDeviceTaskHelper
* task_helper
=
139 GetDeviceTaskHelperForStorage(storage_name
);
142 task_helper
->WriteDataIntoSnapshotFile(request_info
, snapshot_file_info
);
145 // Copies the contents of |device_file_path| to |snapshot_file_path|.
147 // Called on the UI thread to dispatch the request to the
148 // MediaTransferProtocolManager.
150 // |storage_name| specifies the name of the storage device.
151 // |request| is a struct containing details about the byte read request.
152 void ReadBytesOnUIThread(
153 const std::string
& storage_name
,
154 const MTPDeviceAsyncDelegate::ReadBytesRequest
& request
) {
155 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
156 MTPDeviceTaskHelper
* task_helper
=
157 GetDeviceTaskHelperForStorage(storage_name
);
160 task_helper
->ReadBytes(request
);
163 // Closes the device storage specified by the |storage_name| and destroys the
164 // MTPDeviceTaskHelper object associated with the device storage.
166 // Called on the UI thread to dispatch the request to the
167 // MediaTransferProtocolManager.
168 void CloseStorageAndDestroyTaskHelperOnUIThread(
169 const std::string
& storage_name
) {
170 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
171 MTPDeviceTaskHelper
* task_helper
=
172 GetDeviceTaskHelperForStorage(storage_name
);
175 task_helper
->CloseStorage();
176 MTPDeviceTaskHelperMapService::GetInstance()->DestroyDeviceTaskHelper(
182 MTPDeviceDelegateImplLinux::PendingTaskInfo::PendingTaskInfo(
183 const tracked_objects::Location
& location
,
184 const base::Closure
& task
)
185 : location(location
),
189 MTPDeviceDelegateImplLinux::PendingTaskInfo::~PendingTaskInfo() {
192 MTPDeviceDelegateImplLinux::MTPDeviceDelegateImplLinux(
193 const std::string
& device_location
)
194 : init_state_(UNINITIALIZED
),
195 task_in_progress_(false),
196 device_path_(device_location
),
197 weak_ptr_factory_(this) {
198 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
199 DCHECK(!device_path_
.empty());
200 base::RemoveChars(device_location
, kRootPath
, &storage_name_
);
201 DCHECK(!storage_name_
.empty());
204 MTPDeviceDelegateImplLinux::~MTPDeviceDelegateImplLinux() {
205 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
208 void MTPDeviceDelegateImplLinux::GetFileInfo(
209 const base::FilePath
& file_path
,
210 const GetFileInfoSuccessCallback
& success_callback
,
211 const ErrorCallback
& error_callback
) {
212 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
213 DCHECK(!file_path
.empty());
214 base::Closure call_closure
=
215 base::Bind(&GetFileInfoOnUIThread
,
217 GetDeviceRelativePath(device_path_
, file_path
),
218 base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfo
,
219 weak_ptr_factory_
.GetWeakPtr(),
221 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
222 weak_ptr_factory_
.GetWeakPtr(),
224 EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE
, call_closure
));
227 void MTPDeviceDelegateImplLinux::ReadDirectory(
228 const base::FilePath
& root
,
229 const ReadDirectorySuccessCallback
& success_callback
,
230 const ErrorCallback
& error_callback
) {
231 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
232 DCHECK(!root
.empty());
233 std::string device_file_relative_path
= GetDeviceRelativePath(device_path_
,
235 base::Closure call_closure
=
237 &GetFileInfoOnUIThread
,
239 device_file_relative_path
,
241 &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory
,
242 weak_ptr_factory_
.GetWeakPtr(),
243 device_file_relative_path
,
246 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
247 weak_ptr_factory_
.GetWeakPtr(),
249 EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE
, call_closure
));
252 void MTPDeviceDelegateImplLinux::CreateSnapshotFile(
253 const base::FilePath
& device_file_path
,
254 const base::FilePath
& snapshot_file_path
,
255 const CreateSnapshotFileSuccessCallback
& success_callback
,
256 const ErrorCallback
& error_callback
) {
257 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
258 DCHECK(!device_file_path
.empty());
259 DCHECK(!snapshot_file_path
.empty());
260 std::string device_file_relative_path
=
261 GetDeviceRelativePath(device_path_
, device_file_path
);
262 scoped_ptr
<SnapshotRequestInfo
> request_info(
263 new SnapshotRequestInfo(device_file_relative_path
,
267 base::Closure call_closure
=
269 &GetFileInfoOnUIThread
,
271 device_file_relative_path
,
273 &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile
,
274 weak_ptr_factory_
.GetWeakPtr(),
275 base::Passed(&request_info
)),
276 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
277 weak_ptr_factory_
.GetWeakPtr(),
279 EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE
, call_closure
));
282 bool MTPDeviceDelegateImplLinux::IsStreaming() {
286 void MTPDeviceDelegateImplLinux::ReadBytes(
287 const base::FilePath
& device_file_path
,
288 net::IOBuffer
* buf
, int64 offset
, int buf_len
,
289 const ReadBytesSuccessCallback
& success_callback
,
290 const ErrorCallback
& error_callback
) {
291 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
292 DCHECK(!device_file_path
.empty());
293 std::string device_file_relative_path
=
294 GetDeviceRelativePath(device_path_
, device_file_path
);
295 ReadBytesRequest
request(device_file_relative_path
, buf
, offset
, buf_len
,
296 success_callback
, error_callback
);
297 base::Closure call_closure
=
299 &GetFileInfoOnUIThread
,
301 device_file_relative_path
,
302 base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadBytes
,
303 weak_ptr_factory_
.GetWeakPtr(), request
),
304 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
305 weak_ptr_factory_
.GetWeakPtr(), error_callback
));
306 EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE
, call_closure
));
309 void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() {
310 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
311 // To cancel all the pending tasks, destroy the MTPDeviceTaskHelper object.
312 content::BrowserThread::PostTask(
313 content::BrowserThread::UI
,
315 base::Bind(&CloseStorageAndDestroyTaskHelperOnUIThread
, storage_name_
));
319 void MTPDeviceDelegateImplLinux::EnsureInitAndRunTask(
320 const PendingTaskInfo
& task_info
) {
321 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
322 if ((init_state_
== INITIALIZED
) && !task_in_progress_
) {
323 task_in_progress_
= true;
324 content::BrowserThread::PostTask(content::BrowserThread::UI
,
329 pending_tasks_
.push(task_info
);
330 if (init_state_
== UNINITIALIZED
) {
331 init_state_
= PENDING_INIT
;
332 task_in_progress_
= true;
333 content::BrowserThread::PostTask(
334 content::BrowserThread::UI
,
336 base::Bind(&OpenStorageOnUIThread
,
338 base::Bind(&MTPDeviceDelegateImplLinux::OnInitCompleted
,
339 weak_ptr_factory_
.GetWeakPtr())));
343 void MTPDeviceDelegateImplLinux::WriteDataIntoSnapshotFile(
344 const base::File::Info
& file_info
) {
345 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
346 DCHECK(current_snapshot_request_info_
.get());
347 DCHECK_GT(file_info
.size
, 0);
348 task_in_progress_
= true;
349 SnapshotRequestInfo
request_info(
350 current_snapshot_request_info_
->device_file_path
,
351 current_snapshot_request_info_
->snapshot_file_path
,
353 &MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile
,
354 weak_ptr_factory_
.GetWeakPtr()),
356 &MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError
,
357 weak_ptr_factory_
.GetWeakPtr()));
359 base::Closure task_closure
= base::Bind(&WriteDataIntoSnapshotFileOnUIThread
,
363 content::BrowserThread::PostTask(content::BrowserThread::UI
,
368 void MTPDeviceDelegateImplLinux::ProcessNextPendingRequest() {
369 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
370 DCHECK(!task_in_progress_
);
371 if (pending_tasks_
.empty())
374 task_in_progress_
= true;
375 const PendingTaskInfo
& task_info
= pending_tasks_
.front();
376 content::BrowserThread::PostTask(content::BrowserThread::UI
,
379 pending_tasks_
.pop();
382 void MTPDeviceDelegateImplLinux::OnInitCompleted(bool succeeded
) {
383 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
384 init_state_
= succeeded
? INITIALIZED
: UNINITIALIZED
;
385 task_in_progress_
= false;
386 ProcessNextPendingRequest();
389 void MTPDeviceDelegateImplLinux::OnDidGetFileInfo(
390 const GetFileInfoSuccessCallback
& success_callback
,
391 const base::File::Info
& file_info
) {
392 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
393 success_callback
.Run(file_info
);
394 task_in_progress_
= false;
395 ProcessNextPendingRequest();
398 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory(
399 const std::string
& root
,
400 const ReadDirectorySuccessCallback
& success_callback
,
401 const ErrorCallback
& error_callback
,
402 const base::File::Info
& file_info
) {
403 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
404 DCHECK(task_in_progress_
);
405 if (!file_info
.is_directory
) {
406 return HandleDeviceFileError(error_callback
,
407 base::File::FILE_ERROR_NOT_A_DIRECTORY
);
410 base::Closure task_closure
=
411 base::Bind(&ReadDirectoryOnUIThread
,
414 base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadDirectory
,
415 weak_ptr_factory_
.GetWeakPtr(),
417 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
418 weak_ptr_factory_
.GetWeakPtr(),
420 content::BrowserThread::PostTask(content::BrowserThread::UI
,
425 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile(
426 scoped_ptr
<SnapshotRequestInfo
> snapshot_request_info
,
427 const base::File::Info
& file_info
) {
428 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
429 DCHECK(!current_snapshot_request_info_
.get());
430 DCHECK(snapshot_request_info
.get());
431 DCHECK(task_in_progress_
);
432 base::File::Error error
= base::File::FILE_OK
;
433 if (file_info
.is_directory
)
434 error
= base::File::FILE_ERROR_NOT_A_FILE
;
435 else if (file_info
.size
< 0 || file_info
.size
> kuint32max
)
436 error
= base::File::FILE_ERROR_FAILED
;
438 if (error
!= base::File::FILE_OK
)
439 return HandleDeviceFileError(snapshot_request_info
->error_callback
, error
);
441 base::File::Info
snapshot_file_info(file_info
);
442 // Modify the last modified time to null. This prevents the time stamp
443 // verfication in LocalFileStreamReader.
444 snapshot_file_info
.last_modified
= base::Time();
446 current_snapshot_request_info_
.reset(snapshot_request_info
.release());
447 if (file_info
.size
== 0) {
448 // Empty snapshot file.
449 return OnDidWriteDataIntoSnapshotFile(
450 snapshot_file_info
, current_snapshot_request_info_
->snapshot_file_path
);
452 WriteDataIntoSnapshotFile(snapshot_file_info
);
455 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadBytes(
456 const ReadBytesRequest
& request
,
457 const base::File::Info
& file_info
) {
458 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
460 DCHECK(request
.buf_len
>= 0);
461 DCHECK(task_in_progress_
);
462 base::File::Error error
= base::File::FILE_OK
;
463 if (file_info
.is_directory
)
464 error
= base::File::FILE_ERROR_NOT_A_FILE
;
465 else if (file_info
.size
< 0 || file_info
.size
> kuint32max
)
466 error
= base::File::FILE_ERROR_FAILED
;
468 if (error
!= base::File::FILE_OK
)
469 return HandleDeviceFileError(request
.error_callback
, error
);
471 ReadBytesRequest
new_request(
472 request
.device_file_relative_path
,
476 base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadBytes
,
477 weak_ptr_factory_
.GetWeakPtr(), request
.success_callback
),
478 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
479 weak_ptr_factory_
.GetWeakPtr(), request
.error_callback
));
481 content::BrowserThread::PostTask(
482 content::BrowserThread::UI
,
484 base::Bind(&ReadBytesOnUIThread
, storage_name_
, new_request
));
487 void MTPDeviceDelegateImplLinux::OnDidReadDirectory(
488 const ReadDirectorySuccessCallback
& success_callback
,
489 const fileapi::AsyncFileUtil::EntryList
& file_list
) {
490 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
491 success_callback
.Run(file_list
, false /*no more entries*/);
492 task_in_progress_
= false;
493 ProcessNextPendingRequest();
496 void MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile(
497 const base::File::Info
& file_info
,
498 const base::FilePath
& snapshot_file_path
) {
499 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
500 DCHECK(current_snapshot_request_info_
.get());
501 DCHECK(task_in_progress_
);
502 current_snapshot_request_info_
->success_callback
.Run(
503 file_info
, snapshot_file_path
);
504 task_in_progress_
= false;
505 current_snapshot_request_info_
.reset();
506 ProcessNextPendingRequest();
509 void MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError(
510 base::File::Error error
) {
511 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
512 DCHECK(current_snapshot_request_info_
.get());
513 DCHECK(task_in_progress_
);
514 current_snapshot_request_info_
->error_callback
.Run(error
);
515 task_in_progress_
= false;
516 current_snapshot_request_info_
.reset();
517 ProcessNextPendingRequest();
520 void MTPDeviceDelegateImplLinux::OnDidReadBytes(
521 const ReadBytesSuccessCallback
& success_callback
,
523 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
524 DCHECK(task_in_progress_
);
525 success_callback
.Run(bytes_read
);
526 task_in_progress_
= false;
527 ProcessNextPendingRequest();
530 void MTPDeviceDelegateImplLinux::HandleDeviceFileError(
531 const ErrorCallback
& error_callback
,
532 base::File::Error error
) {
533 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
534 DCHECK(task_in_progress_
);
535 error_callback
.Run(error
);
536 task_in_progress_
= false;
537 ProcessNextPendingRequest();
540 void CreateMTPDeviceAsyncDelegate(
541 const std::string
& device_location
,
542 const CreateMTPDeviceAsyncDelegateCallback
& callback
) {
543 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
544 callback
.Run(new MTPDeviceDelegateImplLinux(device_location
));