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"
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
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
)
32 base::FilePath relative_path
;
33 if (!registered_dev_path
.AppendRelativePath(file_path
, &relative_path
))
35 DCHECK(!relative_path
.empty());
36 return relative_path
.value();
39 // Returns the MTPDeviceTaskHelper object associated with the MTP device
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(
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
);
68 MTPDeviceTaskHelperMapService::GetInstance()->CreateDeviceTaskHelper(
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
,
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
);
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
);
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
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
);
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
);
156 task_helper
->CloseStorage();
157 MTPDeviceTaskHelperMapService::GetInstance()->DestroyDeviceTaskHelper(
163 MTPDeviceDelegateImplLinux::PendingTaskInfo::PendingTaskInfo(
164 const tracked_objects::Location
& location
,
165 const base::Closure
& task
)
166 : location(location
),
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
,
198 GetDeviceRelativePath(device_path_
, file_path
),
199 base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfo
,
200 weak_ptr_factory_
.GetWeakPtr(),
202 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
203 weak_ptr_factory_
.GetWeakPtr(),
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_
,
216 base::Closure call_closure
=
218 &GetFileInfoOnUIThread
,
220 device_file_relative_path
,
222 &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory
,
223 weak_ptr_factory_
.GetWeakPtr(),
224 device_file_relative_path
,
227 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
228 weak_ptr_factory_
.GetWeakPtr(),
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
,
248 base::Closure call_closure
=
250 &GetFileInfoOnUIThread
,
252 device_file_relative_path
,
254 &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile
,
255 weak_ptr_factory_
.GetWeakPtr(),
256 base::Passed(&request_info
)),
257 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
258 weak_ptr_factory_
.GetWeakPtr(),
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
,
269 base::Bind(&CloseStorageAndDestroyTaskHelperOnUIThread
, storage_name_
));
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
,
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
,
290 base::Bind(&OpenStorageOnUIThread
,
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
,
307 &MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile
,
308 weak_ptr_factory_
.GetWeakPtr()),
310 &MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError
,
311 weak_ptr_factory_
.GetWeakPtr()));
313 base::Closure task_closure
= base::Bind(&WriteDataIntoSnapshotFileOnUIThread
,
317 content::BrowserThread::PostTask(content::BrowserThread::UI
,
322 void MTPDeviceDelegateImplLinux::ProcessNextPendingRequest() {
323 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
324 DCHECK(!task_in_progress_
);
325 if (pending_tasks_
.empty())
328 task_in_progress_
= true;
329 const PendingTaskInfo
& task_info
= pending_tasks_
.front();
330 content::BrowserThread::PostTask(content::BrowserThread::UI
,
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
,
368 base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadDirectory
,
369 weak_ptr_factory_
.GetWeakPtr(),
371 base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError
,
372 weak_ptr_factory_
.GetWeakPtr(),
374 content::BrowserThread::PostTask(content::BrowserThread::UI
,
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
));