OLD | NEW |
| (Empty) |
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_gallery/mtp_device_delegate_impl_linux.h" | |
6 | |
7 #include <fcntl.h> | |
8 #include <sys/stat.h> | |
9 #include <sys/types.h> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/file_path.h" | |
13 #include "base/file_util.h" | |
14 #include "base/sequenced_task_runner.h" | |
15 #include "base/sequenced_task_runner_helpers.h" | |
16 #include "base/string_util.h" | |
17 #include "base/synchronization/cancellation_flag.h" | |
18 #include "base/threading/sequenced_worker_pool.h" | |
19 #include "chrome/browser/media_transfer_protocol/media_transfer_protocol_manager
.h" | |
20 #include "chrome/browser/media_transfer_protocol/mtp_file_entry.pb.h" | |
21 #include "content/public/browser/browser_thread.h" | |
22 #include "third_party/cros_system_api/dbus/service_constants.h" | |
23 | |
24 using base::Bind; | |
25 using base::PlatformFileError; | |
26 using base::PlatformFileInfo; | |
27 using base::SequencedTaskRunner; | |
28 using base::Time; | |
29 using content::BrowserThread; | |
30 using fileapi::FileSystemFileUtil; | |
31 | |
32 namespace chrome { | |
33 | |
34 namespace { | |
35 | |
36 using base::DeleteHelper; | |
37 using base::RefCountedThreadSafe; | |
38 using base::WaitableEvent; | |
39 | |
40 // Helper struct to delete worker objects on |media_task_runner_| thread. | |
41 template <typename WORKER> struct WorkerDeleter { | |
42 static void Destruct(const WORKER* worker) { | |
43 if (!worker->media_task_runner()->RunsTasksOnCurrentThread()) { | |
44 worker->media_task_runner()->DeleteSoon(FROM_HERE, worker); | |
45 return; | |
46 } | |
47 delete worker; | |
48 } | |
49 }; | |
50 | |
51 typedef struct WorkerDeleter<class GetFileInfoWorker> GetFileInfoWorkerDeleter; | |
52 typedef struct WorkerDeleter<class OpenStorageWorker> OpenStorageWorkerDeleter; | |
53 typedef struct WorkerDeleter<class ReadDirectoryWorker> | |
54 ReadDirectoryWorkerDeleter; | |
55 typedef struct WorkerDeleter<class ReadFileWorker> ReadFileWorkerDeleter; | |
56 | |
57 // File path separator constant. | |
58 const char kRootPath[] = "/"; | |
59 | |
60 // Returns MediaTransferProtocolManager instance on success or NULL on failure. | |
61 MediaTransferProtocolManager* GetMediaTransferProtocolManager() { | |
62 MediaTransferProtocolManager* mtp_device_mgr = | |
63 MediaTransferProtocolManager::GetInstance(); | |
64 DCHECK(mtp_device_mgr); | |
65 return mtp_device_mgr; | |
66 } | |
67 | |
68 // Does nothing. | |
69 // This method is used to handle the results of | |
70 // MediaTransferProtocolManager::CloseStorage method call. | |
71 void DoNothing(bool error) { | |
72 } | |
73 | |
74 // Closes the device storage on the UI thread. | |
75 void CloseStorageOnUIThread(const std::string& device_handle) { | |
76 DCHECK(!device_handle.empty()); | |
77 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
78 GetMediaTransferProtocolManager()->CloseStorage(device_handle, | |
79 Bind(&DoNothing)); | |
80 } | |
81 | |
82 // Returns the device relative file path given |file_path|. | |
83 // E.g.: If the |file_path| is "/usb:2,2:12345/DCIM" and |registered_dev_path| | |
84 // is "/usb:2,2:12345", this function returns the device relative path which is | |
85 // "/DCIM". | |
86 std::string GetDeviceRelativePath(const std::string& registered_dev_path, | |
87 const std::string& file_path) { | |
88 DCHECK(!registered_dev_path.empty()); | |
89 DCHECK(!file_path.empty()); | |
90 | |
91 std::string actual_file_path; | |
92 if (registered_dev_path == file_path) { | |
93 actual_file_path = kRootPath; | |
94 } else { | |
95 actual_file_path = file_path; | |
96 ReplaceFirstSubstringAfterOffset(&actual_file_path, 0, | |
97 registered_dev_path.c_str(), ""); | |
98 } | |
99 DCHECK(!actual_file_path.empty()); | |
100 return actual_file_path; | |
101 } | |
102 | |
103 // Worker class to open a MTP device for communication. This class is | |
104 // instantiated and destructed on |media_task_runner_|. In order to post a | |
105 // request on Dbus thread, the caller should run on UI thread. Therefore, this | |
106 // class posts the open device request on UI thread and receives the response | |
107 // on UI thread. | |
108 class OpenStorageWorker | |
109 : public RefCountedThreadSafe<OpenStorageWorker, OpenStorageWorkerDeleter> { | |
110 public: | |
111 // Constructed on |media_task_runner_| thread. | |
112 OpenStorageWorker(const std::string& name, SequencedTaskRunner* task_runner, | |
113 WaitableEvent* task_completed_event, | |
114 WaitableEvent* shutdown_event) | |
115 : storage_name_(name), | |
116 media_task_runner_(task_runner), | |
117 on_task_completed_event_(task_completed_event), | |
118 on_shutdown_event_(shutdown_event) { | |
119 DCHECK(on_task_completed_event_); | |
120 DCHECK(on_shutdown_event_); | |
121 } | |
122 | |
123 // This function is invoked on |media_task_runner_| to post the task on UI | |
124 // thread. This blocks the |media_task_runner_| until the task is complete. | |
125 void Run() { | |
126 if (on_shutdown_event_->IsSignaled()) { | |
127 // Process is in shutdown mode. | |
128 // Do not post any task on |media_task_runner_|. | |
129 return; | |
130 } | |
131 | |
132 DCHECK(media_task_runner_->RunsTasksOnCurrentThread()); | |
133 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
134 Bind(&OpenStorageWorker::DoWorkOnUIThread, this)); | |
135 on_task_completed_event_->Wait(); | |
136 | |
137 if (on_shutdown_event_->IsSignaled()) | |
138 cancel_tasks_flag_.Set(); | |
139 } | |
140 | |
141 // Returns a device handle string if the OpenStorage() request was | |
142 // successfully completed or an empty string otherwise. | |
143 const std::string& device_handle() const { return device_handle_; } | |
144 | |
145 // Returns the |media_task_runner_| associated with this worker object. | |
146 // This function is exposed for WorkerDeleter struct to access the | |
147 // |media_task_runner_|. | |
148 SequencedTaskRunner* media_task_runner() const { | |
149 return media_task_runner_.get(); | |
150 } | |
151 | |
152 private: | |
153 friend struct WorkerDeleter<OpenStorageWorker>; | |
154 friend class DeleteHelper<OpenStorageWorker>; | |
155 friend class RefCountedThreadSafe<OpenStorageWorker, | |
156 OpenStorageWorkerDeleter>; | |
157 | |
158 // Destructed via OpenStorageWorkerDeleter struct. | |
159 virtual ~OpenStorageWorker() { | |
160 // This object must be destructed on |media_task_runner_|. | |
161 } | |
162 | |
163 // Dispatches a request to MediaTransferProtocolManager to open the MTP | |
164 // storage for communication. This is called on UI thread. | |
165 void DoWorkOnUIThread() { | |
166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
167 if (cancel_tasks_flag_.IsSet()) | |
168 return; | |
169 | |
170 GetMediaTransferProtocolManager()->OpenStorage( | |
171 storage_name_, mtpd::kReadOnlyMode, | |
172 Bind(&OpenStorageWorker::OnDidWorkOnUIThread, this)); | |
173 } | |
174 | |
175 // Query callback for DoWorkOnUIThread(). |error| is set to true if the device | |
176 // did not open successfully. This function signals to unblock | |
177 // |media_task_runner_|. | |
178 void OnDidWorkOnUIThread(const std::string& device_handle, bool error) { | |
179 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
180 if (cancel_tasks_flag_.IsSet()) | |
181 return; | |
182 | |
183 if (!error) | |
184 device_handle_ = device_handle; | |
185 on_task_completed_event_->Signal(); | |
186 } | |
187 | |
188 // Stores the storage name to open the device. | |
189 const std::string storage_name_; | |
190 | |
191 // Stores a reference to |media_task_runner_| to destruct this object on the | |
192 // correct thread. | |
193 scoped_refptr<SequencedTaskRunner> media_task_runner_; | |
194 | |
195 // |media_task_runner_| can wait on this event until the required operation | |
196 // is complete. | |
197 // TODO(kmadhusu): Remove this WaitableEvent after modifying the | |
198 // DeviceMediaFileUtil functions as asynchronous functions. | |
199 WaitableEvent* on_task_completed_event_; | |
200 | |
201 // Stores a reference to waitable event associated with the shut down message. | |
202 WaitableEvent* on_shutdown_event_; | |
203 | |
204 // Stores the result of OpenStorage() request. | |
205 std::string device_handle_; | |
206 | |
207 // Set to ignore the request results. This will be set when | |
208 // MTPDeviceDelegateImplLinux object is about to be deleted. | |
209 // |on_task_completed_event_| and |on_shutdown_event_| should not be | |
210 // dereferenced when this is set. | |
211 base::CancellationFlag cancel_tasks_flag_; | |
212 | |
213 DISALLOW_COPY_AND_ASSIGN(OpenStorageWorker); | |
214 }; | |
215 | |
216 // Worker class to get media device file information given a |path|. | |
217 class GetFileInfoWorker | |
218 : public RefCountedThreadSafe<GetFileInfoWorker, GetFileInfoWorkerDeleter> { | |
219 public: | |
220 // Constructed on |media_task_runner_| thread. | |
221 GetFileInfoWorker(const std::string& handle, | |
222 const std::string& path, | |
223 SequencedTaskRunner* task_runner, | |
224 WaitableEvent* task_completed_event, | |
225 WaitableEvent* shutdown_event) | |
226 : device_handle_(handle), | |
227 path_(path), | |
228 media_task_runner_(task_runner), | |
229 error_(base::PLATFORM_FILE_OK), | |
230 on_task_completed_event_(task_completed_event), | |
231 on_shutdown_event_(shutdown_event) { | |
232 DCHECK(on_task_completed_event_); | |
233 DCHECK(on_shutdown_event_); | |
234 } | |
235 | |
236 // This function is invoked on |media_task_runner_| to post the task on UI | |
237 // thread. This blocks the |media_task_runner_| until the task is complete. | |
238 void Run() { | |
239 if (on_shutdown_event_->IsSignaled()) { | |
240 // Process is in shutdown mode. | |
241 // Do not post any task on |media_task_runner_|. | |
242 return; | |
243 } | |
244 | |
245 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
246 Bind(&GetFileInfoWorker::DoWorkOnUIThread, this)); | |
247 on_task_completed_event_->Wait(); | |
248 | |
249 if (on_shutdown_event_->IsSignaled()) | |
250 cancel_tasks_flag_.Set(); | |
251 } | |
252 | |
253 // Returns GetFileInfo() result and fills in |file_info| with requested file | |
254 // entry details. | |
255 PlatformFileError get_file_info(PlatformFileInfo* file_info) const { | |
256 if (file_info) | |
257 *file_info = file_entry_info_; | |
258 return error_; | |
259 } | |
260 | |
261 // Returns the |media_task_runner_| associated with this worker object. | |
262 // This function is exposed for WorkerDeleter struct to access the | |
263 // |media_task_runner_|. | |
264 SequencedTaskRunner* media_task_runner() const { | |
265 return media_task_runner_.get(); | |
266 } | |
267 | |
268 private: | |
269 friend struct WorkerDeleter<GetFileInfoWorker>; | |
270 friend class DeleteHelper<GetFileInfoWorker>; | |
271 friend class RefCountedThreadSafe<GetFileInfoWorker, | |
272 GetFileInfoWorkerDeleter>; | |
273 | |
274 // Destructed via GetFileInfoWorkerDeleter. | |
275 virtual ~GetFileInfoWorker() { | |
276 // This object must be destructed on |media_task_runner_|. | |
277 } | |
278 | |
279 // Dispatches a request to MediaTransferProtocolManager to get file | |
280 // information. | |
281 void DoWorkOnUIThread() { | |
282 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
283 if (cancel_tasks_flag_.IsSet()) { | |
284 error_ = base::PLATFORM_FILE_ERROR_FAILED; | |
285 return; | |
286 } | |
287 | |
288 GetMediaTransferProtocolManager()->GetFileInfoByPath( | |
289 device_handle_, path_, | |
290 Bind(&GetFileInfoWorker::OnDidWorkOnUIThread, this)); | |
291 } | |
292 | |
293 // Query callback for DoWorkOnUIThread(). On success, |file_entry| has media | |
294 // file information. On failure, |error| is set to true. This function signals | |
295 // to unblock |media_task_runner_|. | |
296 void OnDidWorkOnUIThread(const MtpFileEntry& file_entry, bool error) { | |
297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
298 if (cancel_tasks_flag_.IsSet()) { | |
299 error_ = base::PLATFORM_FILE_ERROR_FAILED; | |
300 return; | |
301 } | |
302 | |
303 if (error) { | |
304 error_ = base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
305 } else { | |
306 file_entry_info_.size = file_entry.file_size(); | |
307 file_entry_info_.is_directory = | |
308 file_entry.file_type() == MtpFileEntry::FILE_TYPE_FOLDER; | |
309 file_entry_info_.is_symbolic_link = false; | |
310 file_entry_info_.last_modified = | |
311 base::Time::FromTimeT(file_entry.modification_time()); | |
312 file_entry_info_.last_accessed = | |
313 base::Time::FromTimeT(file_entry.modification_time()); | |
314 file_entry_info_.creation_time = base::Time(); | |
315 } | |
316 on_task_completed_event_->Signal(); | |
317 } | |
318 | |
319 // Stores the device handle to query the device. | |
320 const std::string device_handle_; | |
321 | |
322 // Stores the requested media device file path. | |
323 const std::string path_; | |
324 | |
325 // Stores a reference to |media_task_runner_| to destruct this object on the | |
326 // correct thread. | |
327 scoped_refptr<SequencedTaskRunner> media_task_runner_; | |
328 | |
329 // Stores the result of GetFileInfo(). | |
330 PlatformFileError error_; | |
331 | |
332 // Stores the media file entry information. | |
333 PlatformFileInfo file_entry_info_; | |
334 | |
335 // |media_task_runner_| can wait on this event until the required operation | |
336 // is complete. | |
337 // TODO(kmadhusu): Remove this WaitableEvent after modifying the | |
338 // DeviceMediaFileUtil functions as asynchronous functions. | |
339 WaitableEvent* on_task_completed_event_; | |
340 | |
341 // Stores a reference to waitable event associated with the shut down message. | |
342 WaitableEvent* on_shutdown_event_; | |
343 | |
344 // Set to ignore the request results. This will be set when | |
345 // MTPDeviceDelegateImplLinux object is about to be deleted. | |
346 // |on_task_completed_event_| and |on_shutdown_event_| should not be | |
347 // dereferenced when this is set. | |
348 base::CancellationFlag cancel_tasks_flag_; | |
349 | |
350 DISALLOW_COPY_AND_ASSIGN(GetFileInfoWorker); | |
351 }; | |
352 | |
353 // Worker class to read media device file data given a file |path|. | |
354 class ReadFileWorker | |
355 : public RefCountedThreadSafe<ReadFileWorker, ReadFileWorkerDeleter> { | |
356 public: | |
357 // Constructed on |media_task_runner_| thread. | |
358 ReadFileWorker(const std::string& handle, | |
359 const std::string& src_path, | |
360 uint32 total_size, | |
361 const FilePath& dest_path, | |
362 SequencedTaskRunner* task_runner, | |
363 WaitableEvent* task_completed_event, | |
364 WaitableEvent* shutdown_event) | |
365 : device_handle_(handle), | |
366 src_path_(src_path), | |
367 total_bytes_(total_size), | |
368 dest_path_(dest_path), | |
369 bytes_read_(0), | |
370 error_occurred_(false), | |
371 media_task_runner_(task_runner), | |
372 on_task_completed_event_(task_completed_event), | |
373 on_shutdown_event_(shutdown_event) { | |
374 DCHECK(on_task_completed_event_); | |
375 DCHECK(on_shutdown_event_); | |
376 } | |
377 | |
378 // This function is invoked on |media_task_runner_| to post the task on UI | |
379 // thread. This blocks the |media_task_runner_| until the task is complete. | |
380 void Run() { | |
381 if (on_shutdown_event_->IsSignaled()) { | |
382 // Process is in shutdown mode. | |
383 // Do not post any task on |media_task_runner_|. | |
384 return; | |
385 } | |
386 | |
387 int dest_fd = open(dest_path_.value().c_str(), O_WRONLY); | |
388 if (dest_fd < 0) | |
389 return; | |
390 file_util::ScopedFD dest_fd_scoper(&dest_fd); | |
391 | |
392 while (bytes_read_ < total_bytes_) { | |
393 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
394 Bind(&ReadFileWorker::DoWorkOnUIThread, this)); | |
395 on_task_completed_event_->Wait(); | |
396 if (error_occurred_) | |
397 break; | |
398 if (on_shutdown_event_->IsSignaled()) { | |
399 cancel_tasks_flag_.Set(); | |
400 break; | |
401 } | |
402 | |
403 int bytes_written = | |
404 file_util::WriteFileDescriptor(dest_fd, data_.data(), data_.size()); | |
405 if (static_cast<int>(data_.size()) != bytes_written) | |
406 break; | |
407 | |
408 bytes_read_ += data_.size(); | |
409 } | |
410 } | |
411 | |
412 bool Succeeded() const { | |
413 return !error_occurred_ && (bytes_read_ == total_bytes_); | |
414 } | |
415 | |
416 // Returns the |media_task_runner_| associated with this worker object. | |
417 // This function is exposed for WorkerDeleter struct to access the | |
418 // |media_task_runner_|. | |
419 SequencedTaskRunner* media_task_runner() const { | |
420 return media_task_runner_.get(); | |
421 } | |
422 | |
423 private: | |
424 friend struct WorkerDeleter<ReadFileWorker>; | |
425 friend class DeleteHelper<ReadFileWorker>; | |
426 friend class RefCountedThreadSafe<ReadFileWorker, ReadFileWorkerDeleter>; | |
427 | |
428 // Destructed via ReadFileWorkerDeleter. | |
429 virtual ~ReadFileWorker() { | |
430 // This object must be destructed on |media_task_runner_|. | |
431 } | |
432 | |
433 // Dispatches a request to MediaTransferProtocolManager to get the media file | |
434 // contents. | |
435 void DoWorkOnUIThread() { | |
436 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
437 if (cancel_tasks_flag_.IsSet()) | |
438 return; | |
439 | |
440 GetMediaTransferProtocolManager()->ReadFileChunkByPath( | |
441 device_handle_, src_path_, bytes_read_, BytesToRead(), | |
442 Bind(&ReadFileWorker::OnDidWorkOnUIThread, this)); | |
443 } | |
444 | |
445 // Query callback for DoWorkOnUIThread(). On success, |data| has the media | |
446 // file contents. On failure, |error| is set to true. This function signals | |
447 // to unblock |media_task_runner_|. | |
448 void OnDidWorkOnUIThread(const std::string& data, bool error) { | |
449 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
450 if (cancel_tasks_flag_.IsSet()) | |
451 return; | |
452 | |
453 error_occurred_ = error || (data.size() != BytesToRead()); | |
454 if (!error_occurred_) | |
455 data_ = data; | |
456 on_task_completed_event_->Signal(); | |
457 } | |
458 | |
459 uint32 BytesToRead() const { | |
460 // Read data in 1 MB chunks. | |
461 static const uint32 kReadChunkSize = 1024 * 1024; | |
462 return std::min(kReadChunkSize, total_bytes_ - bytes_read_); | |
463 } | |
464 | |
465 // The device unique identifier to query the device. | |
466 const std::string device_handle_; | |
467 | |
468 // The media device file path. | |
469 const std::string src_path_; | |
470 | |
471 // Number of bytes to read. | |
472 const uint32 total_bytes_; | |
473 | |
474 // Where to write the data read from the device. | |
475 const FilePath dest_path_; | |
476 | |
477 /***************************************************************************** | |
478 * The variables below are accessed on both |media_task_runner_| and the UI | |
479 * thread. However, there's no concurrent access because the UI thread is in a | |
480 * blocked state when access occurs on |media_task_runner_|. | |
481 */ | |
482 | |
483 // Number of bytes read from the device. | |
484 uint32 bytes_read_; | |
485 | |
486 // Temporary data storage. | |
487 std::string data_; | |
488 | |
489 // Whether an error occurred during file transfer. | |
490 bool error_occurred_; | |
491 | |
492 /****************************************************************************/ | |
493 | |
494 // A reference to |media_task_runner_| to destruct this object on the correct | |
495 // thread. | |
496 scoped_refptr<SequencedTaskRunner> media_task_runner_; | |
497 | |
498 // |media_task_runner_| can wait on this event until the required operation | |
499 // is complete. | |
500 // TODO(kmadhusu): Remove this WaitableEvent after modifying the | |
501 // DeviceMediaFileUtil functions as asynchronous functions. | |
502 WaitableEvent* on_task_completed_event_; | |
503 | |
504 // Stores a reference to waitable event associated with the shut down message. | |
505 WaitableEvent* on_shutdown_event_; | |
506 | |
507 // Set to ignore the request results. This will be set when | |
508 // MTPDeviceDelegateImplLinux object is about to be deleted. | |
509 // |on_task_completed_event_| and |on_shutdown_event_| should not be | |
510 // dereferenced when this is set. | |
511 base::CancellationFlag cancel_tasks_flag_; | |
512 | |
513 DISALLOW_COPY_AND_ASSIGN(ReadFileWorker); | |
514 }; | |
515 | |
516 // Worker class to read directory contents. Device is already opened for | |
517 // communication. | |
518 class ReadDirectoryWorker | |
519 : public RefCountedThreadSafe<ReadDirectoryWorker, | |
520 ReadDirectoryWorkerDeleter> { | |
521 public: | |
522 // Construct a worker object given the directory |path|. This object is | |
523 // constructed on |media_task_runner_| thread. | |
524 ReadDirectoryWorker(const std::string& handle, | |
525 const std::string& path, | |
526 SequencedTaskRunner* task_runner, | |
527 WaitableEvent* task_completed_event, | |
528 WaitableEvent* shutdown_event) | |
529 : device_handle_(handle), | |
530 dir_path_(path), | |
531 dir_entry_id_(0), | |
532 media_task_runner_(task_runner), | |
533 on_task_completed_event_(task_completed_event), | |
534 on_shutdown_event_(shutdown_event) { | |
535 DCHECK(!dir_path_.empty()); | |
536 DCHECK(on_task_completed_event_); | |
537 DCHECK(on_shutdown_event_); | |
538 } | |
539 | |
540 // Construct a worker object given the directory |entry_id|. This object is | |
541 // constructed on |media_task_runner_| thread. | |
542 ReadDirectoryWorker(const std::string& storage_name, | |
543 const uint32_t entry_id, | |
544 SequencedTaskRunner* task_runner, | |
545 WaitableEvent* task_completed_event, | |
546 WaitableEvent* shutdown_event) | |
547 : device_handle_(storage_name), | |
548 dir_entry_id_(entry_id), | |
549 media_task_runner_(task_runner), | |
550 on_task_completed_event_(task_completed_event), | |
551 on_shutdown_event_(shutdown_event) { | |
552 DCHECK(on_task_completed_event_); | |
553 DCHECK(on_shutdown_event_); | |
554 } | |
555 | |
556 // This function is invoked on |media_task_runner_| to post the task on UI | |
557 // thread. This blocks the |media_task_runner_| until the task is complete. | |
558 void Run() { | |
559 if (on_shutdown_event_->IsSignaled()) { | |
560 // Process is in shutdown mode. | |
561 // Do not post any task on |media_task_runner_|. | |
562 return; | |
563 } | |
564 | |
565 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
566 Bind(&ReadDirectoryWorker::DoWorkOnUIThread, this)); | |
567 on_task_completed_event_->Wait(); | |
568 if (on_shutdown_event_->IsSignaled()) | |
569 cancel_tasks_flag_.Set(); | |
570 } | |
571 | |
572 // Returns the directory entries for the given directory path. | |
573 const std::vector<MtpFileEntry>& get_file_entries() const { | |
574 return file_entries_; | |
575 } | |
576 | |
577 // Returns the |media_task_runner_| associated with this worker object. | |
578 // This function is exposed for WorkerDeleter struct to access the | |
579 // |media_task_runner_|. | |
580 SequencedTaskRunner* media_task_runner() const { | |
581 return media_task_runner_.get(); | |
582 } | |
583 | |
584 private: | |
585 friend struct WorkerDeleter<ReadDirectoryWorker>; | |
586 friend class DeleteHelper<ReadDirectoryWorker>; | |
587 friend class RefCountedThreadSafe<ReadDirectoryWorker, | |
588 ReadDirectoryWorkerDeleter>; | |
589 | |
590 // Destructed via ReadDirectoryWorkerDeleter. | |
591 virtual ~ReadDirectoryWorker() { | |
592 // This object must be destructed on |media_task_runner_|. | |
593 } | |
594 | |
595 // Dispatches a request to MediaTransferProtocolManager to read the directory | |
596 // entries. This is called on UI thread. | |
597 void DoWorkOnUIThread() { | |
598 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
599 if (cancel_tasks_flag_.IsSet()) | |
600 return; | |
601 | |
602 if (!dir_path_.empty()) { | |
603 GetMediaTransferProtocolManager()->ReadDirectoryByPath( | |
604 device_handle_, dir_path_, | |
605 Bind(&ReadDirectoryWorker::OnDidWorkOnUIThread, this)); | |
606 } else { | |
607 GetMediaTransferProtocolManager()->ReadDirectoryById( | |
608 device_handle_, dir_entry_id_, | |
609 Bind(&ReadDirectoryWorker::OnDidWorkOnUIThread, this)); | |
610 } | |
611 } | |
612 | |
613 // Query callback for DoWorkOnUIThread(). On success, |file_entries| has the | |
614 // directory file entries. |error| is true if there was an error. This | |
615 // function signals to unblock |media_task_runner_|. | |
616 void OnDidWorkOnUIThread(const std::vector<MtpFileEntry>& file_entries, | |
617 bool error) { | |
618 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
619 if (cancel_tasks_flag_.IsSet()) | |
620 return; | |
621 | |
622 if (!error) | |
623 file_entries_ = file_entries; | |
624 on_task_completed_event_->Signal(); | |
625 } | |
626 | |
627 // Stores the device handle to communicate with storage device. | |
628 const std::string device_handle_; | |
629 | |
630 // Stores the directory path whose contents needs to be listed. | |
631 const std::string dir_path_; | |
632 | |
633 // Stores the directory entry id whose contents needs to be listed. | |
634 const uint32_t dir_entry_id_; | |
635 | |
636 // Stores a reference to |media_task_runner_| to destruct this object on the | |
637 // correct thread. | |
638 scoped_refptr<SequencedTaskRunner> media_task_runner_; | |
639 | |
640 // |media_task_runner_| can wait on this event until the required operation | |
641 // is complete. | |
642 // TODO(kmadhusu): Remove this WaitableEvent after modifying the | |
643 // DeviceMediaFileUtil functions as asynchronous functions. | |
644 WaitableEvent* on_task_completed_event_; | |
645 | |
646 // Stores a reference to waitable event associated with the shut down message. | |
647 WaitableEvent* on_shutdown_event_; | |
648 | |
649 // Stores the result of read directory request. | |
650 std::vector<MtpFileEntry> file_entries_; | |
651 | |
652 // Set to ignore the request results. This will be set when | |
653 // MTPDeviceDelegateImplLinux object is about to be deleted. | |
654 // |on_task_completed_event_| and |on_shutdown_event_| should not be | |
655 // dereferenced when this is set. | |
656 base::CancellationFlag cancel_tasks_flag_; | |
657 | |
658 DISALLOW_COPY_AND_ASSIGN(ReadDirectoryWorker); | |
659 }; | |
660 | |
661 // Simply enumerate each files from a given file entry list. | |
662 // Used to enumerate top-level files of an media file system. | |
663 class MediaFileEnumerator : public FileSystemFileUtil::AbstractFileEnumerator { | |
664 public: | |
665 explicit MediaFileEnumerator(const std::vector<MtpFileEntry>& entries) | |
666 : file_entries_(entries), | |
667 file_entry_iter_(file_entries_.begin()) { | |
668 } | |
669 | |
670 virtual ~MediaFileEnumerator() {} | |
671 | |
672 // AbstractFileEnumerator override. | |
673 // Returns the next file entry path on success and empty file path on | |
674 // failure. | |
675 virtual FilePath Next() OVERRIDE { | |
676 if (file_entry_iter_ == file_entries_.end()) | |
677 return FilePath(); | |
678 | |
679 current_file_info_ = *file_entry_iter_; | |
680 ++file_entry_iter_; | |
681 return FilePath(current_file_info_.file_name()); | |
682 } | |
683 | |
684 // AbstractFileEnumerator override. | |
685 // Returns the size of the current file entry. | |
686 virtual int64 Size() OVERRIDE { | |
687 return current_file_info_.file_size(); | |
688 } | |
689 | |
690 // AbstractFileEnumerator override. | |
691 // Returns true if the current file entry is a directory else false. | |
692 virtual bool IsDirectory() OVERRIDE { | |
693 return current_file_info_.file_type() == MtpFileEntry::FILE_TYPE_FOLDER; | |
694 } | |
695 | |
696 // AbstractFileEnumerator override. | |
697 // Returns the last modified time of the current file entry. | |
698 virtual base::Time LastModifiedTime() OVERRIDE { | |
699 return base::Time::FromTimeT(current_file_info_.modification_time()); | |
700 } | |
701 | |
702 private: | |
703 // List of directory file entries information. | |
704 const std::vector<MtpFileEntry> file_entries_; | |
705 | |
706 // Iterator to access the individual file entries. | |
707 std::vector<MtpFileEntry>::const_iterator file_entry_iter_; | |
708 | |
709 // Stores the current file information. | |
710 MtpFileEntry current_file_info_; | |
711 | |
712 DISALLOW_COPY_AND_ASSIGN(MediaFileEnumerator); | |
713 }; | |
714 | |
715 // Recursively enumerate each file entry from a given media file entry set. | |
716 class RecursiveMediaFileEnumerator | |
717 : public FileSystemFileUtil::AbstractFileEnumerator { | |
718 public: | |
719 RecursiveMediaFileEnumerator(const std::string& handle, | |
720 SequencedTaskRunner* task_runner, | |
721 const std::vector<MtpFileEntry>& entries, | |
722 WaitableEvent* task_completed_event, | |
723 WaitableEvent* shutdown_event) | |
724 : device_handle_(handle), | |
725 media_task_runner_(task_runner), | |
726 file_entries_(entries), | |
727 file_entry_iter_(file_entries_.begin()), | |
728 on_task_completed_event_(task_completed_event), | |
729 on_shutdown_event_(shutdown_event) { | |
730 DCHECK(on_task_completed_event_); | |
731 DCHECK(on_shutdown_event_); | |
732 current_enumerator_.reset(new MediaFileEnumerator(entries)); | |
733 } | |
734 | |
735 virtual ~RecursiveMediaFileEnumerator() {} | |
736 | |
737 // AbstractFileEnumerator override. | |
738 // Returns the next file entry path on success and empty file path on | |
739 // failure or when it reaches the end. | |
740 virtual FilePath Next() OVERRIDE { | |
741 if (on_shutdown_event_->IsSignaled()) { | |
742 // Process is in shut down mode. | |
743 return FilePath(); | |
744 } | |
745 | |
746 FilePath path = current_enumerator_->Next(); | |
747 if (!path.empty()) | |
748 return path; | |
749 | |
750 // We reached the end. | |
751 if (file_entry_iter_ == file_entries_.end()) | |
752 return FilePath(); | |
753 | |
754 // Enumerate subdirectories of the next media file entry. | |
755 MtpFileEntry next_file_entry = *file_entry_iter_; | |
756 ++file_entry_iter_; | |
757 | |
758 // Create a ReadDirectoryWorker object to enumerate sub directories. | |
759 scoped_refptr<ReadDirectoryWorker> worker(new ReadDirectoryWorker( | |
760 device_handle_, next_file_entry.item_id(), media_task_runner_, | |
761 on_task_completed_event_, on_shutdown_event_)); | |
762 worker->Run(); | |
763 if (!worker->get_file_entries().empty()) { | |
764 current_enumerator_.reset( | |
765 new MediaFileEnumerator(worker->get_file_entries())); | |
766 } else { | |
767 current_enumerator_.reset(new FileSystemFileUtil::EmptyFileEnumerator()); | |
768 } | |
769 return current_enumerator_->Next(); | |
770 } | |
771 | |
772 // AbstractFileEnumerator override. | |
773 // Returns the size of the current file entry. | |
774 virtual int64 Size() OVERRIDE { | |
775 return current_enumerator_->Size(); | |
776 } | |
777 | |
778 // AbstractFileEnumerator override. | |
779 // Returns true if the current media file entry is a folder type else false. | |
780 virtual bool IsDirectory() OVERRIDE { | |
781 return current_enumerator_->IsDirectory(); | |
782 } | |
783 | |
784 // AbstractFileEnumerator override. | |
785 // Returns the last modified time of the current file entry. | |
786 virtual base::Time LastModifiedTime() OVERRIDE { | |
787 return current_enumerator_->LastModifiedTime(); | |
788 } | |
789 | |
790 private: | |
791 // Stores the device handle that was used to open the device. | |
792 const std::string device_handle_; | |
793 | |
794 // Stores a reference to |media_task_runner_| to construct and destruct | |
795 // ReadDirectoryWorker object on the correct thread. | |
796 scoped_refptr<SequencedTaskRunner> media_task_runner_; | |
797 | |
798 // List of top-level directory file entries. | |
799 const std::vector<MtpFileEntry> file_entries_; | |
800 | |
801 // Iterator to access the individual file entries. | |
802 std::vector<MtpFileEntry>::const_iterator file_entry_iter_; | |
803 | |
804 // Enumerator to access current directory Id/path entries. | |
805 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> current_enumerator_; | |
806 | |
807 // |media_task_runner_| can wait on this event until the requested operation | |
808 // is complete. | |
809 WaitableEvent* on_task_completed_event_; | |
810 | |
811 // Stores a reference to waitable event associated with the shut down message. | |
812 WaitableEvent* on_shutdown_event_; | |
813 | |
814 DISALLOW_COPY_AND_ASSIGN(RecursiveMediaFileEnumerator); | |
815 }; | |
816 | |
817 } // namespace | |
818 | |
819 MTPDeviceDelegateImplLinux::MTPDeviceDelegateImplLinux( | |
820 const std::string& device_location) | |
821 : device_path_(device_location), | |
822 on_task_completed_event_(false, false), | |
823 on_shutdown_event_(true, false) { | |
824 CHECK(!device_path_.empty()); | |
825 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
826 base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool(); | |
827 base::SequencedWorkerPool::SequenceToken media_sequence_token = | |
828 pool->GetNamedSequenceToken("media-task-runner"); | |
829 media_task_runner_ = pool->GetSequencedTaskRunner(media_sequence_token); | |
830 } | |
831 | |
832 PlatformFileError MTPDeviceDelegateImplLinux::GetFileInfo( | |
833 const FilePath& file_path, | |
834 PlatformFileInfo* file_info) { | |
835 if (!LazyInit()) | |
836 return base::PLATFORM_FILE_ERROR_FAILED; | |
837 | |
838 scoped_refptr<GetFileInfoWorker> worker(new GetFileInfoWorker( | |
839 device_handle_, GetDeviceRelativePath(device_path_, file_path.value()), | |
840 media_task_runner_, &on_task_completed_event_, &on_shutdown_event_)); | |
841 worker->Run(); | |
842 return worker->get_file_info(file_info); | |
843 } | |
844 | |
845 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> | |
846 MTPDeviceDelegateImplLinux::CreateFileEnumerator( | |
847 const FilePath& root, | |
848 bool recursive) { | |
849 if (root.value().empty() || !LazyInit()) { | |
850 return make_scoped_ptr(new FileSystemFileUtil::EmptyFileEnumerator()) | |
851 .PassAs<FileSystemFileUtil::AbstractFileEnumerator>(); | |
852 } | |
853 | |
854 scoped_refptr<ReadDirectoryWorker> worker(new ReadDirectoryWorker( | |
855 device_handle_, GetDeviceRelativePath(device_path_, root.value()), | |
856 media_task_runner_, &on_task_completed_event_, &on_shutdown_event_)); | |
857 worker->Run(); | |
858 | |
859 if (worker->get_file_entries().empty()) { | |
860 return make_scoped_ptr(new FileSystemFileUtil::EmptyFileEnumerator()) | |
861 .PassAs<FileSystemFileUtil::AbstractFileEnumerator>(); | |
862 } | |
863 | |
864 if (recursive) { | |
865 return make_scoped_ptr(new RecursiveMediaFileEnumerator( | |
866 device_handle_, media_task_runner_, worker->get_file_entries(), | |
867 &on_task_completed_event_, &on_shutdown_event_)) | |
868 .PassAs<FileSystemFileUtil::AbstractFileEnumerator>(); | |
869 } | |
870 return make_scoped_ptr(new MediaFileEnumerator(worker->get_file_entries())) | |
871 .PassAs<FileSystemFileUtil::AbstractFileEnumerator>(); | |
872 } | |
873 | |
874 PlatformFileError MTPDeviceDelegateImplLinux::CreateSnapshotFile( | |
875 const FilePath& device_file_path, | |
876 const FilePath& local_path, | |
877 PlatformFileInfo* file_info) { | |
878 if (!LazyInit()) | |
879 return base::PLATFORM_FILE_ERROR_FAILED; | |
880 | |
881 PlatformFileError error = GetFileInfo(device_file_path, file_info); | |
882 if (error != base::PLATFORM_FILE_OK) | |
883 return error; | |
884 | |
885 if (file_info->size <= 0 || file_info->size > kuint32max) | |
886 return base::PLATFORM_FILE_ERROR_FAILED; | |
887 | |
888 scoped_refptr<ReadFileWorker> worker(new ReadFileWorker( | |
889 device_handle_, | |
890 GetDeviceRelativePath(device_path_, device_file_path.value()), | |
891 file_info->size, local_path, media_task_runner_, | |
892 &on_task_completed_event_, &on_shutdown_event_)); | |
893 worker->Run(); | |
894 | |
895 if (!worker->Succeeded()) | |
896 return base::PLATFORM_FILE_ERROR_FAILED; | |
897 | |
898 // Modify the last modified time to null. This prevents the time stamp | |
899 // verfication in LocalFileStreamReader. | |
900 file_info->last_modified = base::Time(); | |
901 return base::PLATFORM_FILE_OK; | |
902 } | |
903 | |
904 SequencedTaskRunner* MTPDeviceDelegateImplLinux::GetMediaTaskRunner() { | |
905 return media_task_runner_.get(); | |
906 } | |
907 | |
908 void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() { | |
909 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
910 // Caution: This function is called on the IO thread. Access only the thread | |
911 // safe member variables in this function. Do all the clean up operations in | |
912 // DeleteDelegateOnTaskRunner(). | |
913 on_shutdown_event_.Signal(); | |
914 on_task_completed_event_.Signal(); | |
915 media_task_runner_->PostTask( | |
916 FROM_HERE, | |
917 base::Bind(&MTPDeviceDelegateImplLinux::DeleteDelegateOnTaskRunner, | |
918 base::Unretained(this))); | |
919 } | |
920 | |
921 base::WeakPtr<fileapi::MTPDeviceDelegate> MTPDeviceDelegateImplLinux:: | |
922 GetAsWeakPtrOnIOThread() { | |
923 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
924 base::WeakPtr<fileapi::MTPDeviceDelegate> delegate = AsWeakPtr(); | |
925 // The weak pointer is instantiated on the IO thread, but only accessed on | |
926 // |media_task_runner_|. Therefore, detach from the current thread. | |
927 DetachFromThread(); | |
928 return delegate; | |
929 } | |
930 | |
931 MTPDeviceDelegateImplLinux::~MTPDeviceDelegateImplLinux() { | |
932 DCHECK(media_task_runner_->RunsTasksOnCurrentThread()); | |
933 // Do all the clean up operations on DeleteDelegateOnTaskRunner(). | |
934 } | |
935 | |
936 bool MTPDeviceDelegateImplLinux::LazyInit() { | |
937 DCHECK(media_task_runner_); | |
938 DCHECK(media_task_runner_->RunsTasksOnCurrentThread()); | |
939 | |
940 if (!device_handle_.empty()) | |
941 return true; // Already successfully initialized. | |
942 | |
943 std::string storage_name; | |
944 RemoveChars(device_path_, kRootPath, &storage_name); | |
945 DCHECK(!storage_name.empty()); | |
946 scoped_refptr<OpenStorageWorker> worker(new OpenStorageWorker( | |
947 storage_name, media_task_runner_, &on_task_completed_event_, | |
948 &on_shutdown_event_)); | |
949 worker->Run(); | |
950 device_handle_ = worker->device_handle(); | |
951 return !device_handle_.empty(); | |
952 } | |
953 | |
954 void MTPDeviceDelegateImplLinux::DeleteDelegateOnTaskRunner() { | |
955 DCHECK(media_task_runner_->RunsTasksOnCurrentThread()); | |
956 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
957 Bind(&CloseStorageOnUIThread, device_handle_)); | |
958 delete this; | |
959 } | |
960 | |
961 } // namespace chrome | |
OLD | NEW |