Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(62)

Side by Side Diff: chrome/browser/media_gallery/mtp_device_delegate_impl_linux.cc

Issue 11348337: Move MTPDeviceDelegateImplLinux worker classes to its own files. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed review comments Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW
« no previous file with comments | « chrome/browser/media_gallery/mtp_device_delegate_impl_linux.h ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698