Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/media_gallery/mtp_device_delegate_impl_linux.h" | 5 #include "chrome/browser/media_gallery/mtp_device_delegate_impl_linux.h" |
| 6 | 6 |
| 7 #include <fcntl.h> | |
| 8 #include <sys/stat.h> | |
| 9 #include <sys/types.h> | |
| 10 | |
| 7 #include "base/bind.h" | 11 #include "base/bind.h" |
| 8 #include "base/file_path.h" | 12 #include "base/file_path.h" |
| 9 #include "base/file_util.h" | 13 #include "base/file_util.h" |
| 10 #include "base/sequenced_task_runner.h" | 14 #include "base/sequenced_task_runner.h" |
| 11 #include "base/sequenced_task_runner_helpers.h" | 15 #include "base/sequenced_task_runner_helpers.h" |
| 12 #include "base/string_util.h" | 16 #include "base/string_util.h" |
| 13 #include "base/synchronization/cancellation_flag.h" | 17 #include "base/synchronization/cancellation_flag.h" |
| 14 #include "base/threading/sequenced_worker_pool.h" | 18 #include "base/threading/sequenced_worker_pool.h" |
| 15 #include "chrome/browser/media_transfer_protocol/media_transfer_protocol_manager .h" | 19 #include "chrome/browser/media_transfer_protocol/media_transfer_protocol_manager .h" |
| 16 #include "chrome/browser/media_transfer_protocol/mtp_file_entry.pb.h" | 20 #include "chrome/browser/media_transfer_protocol/mtp_file_entry.pb.h" |
| (...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 345 | 349 |
| 346 DISALLOW_COPY_AND_ASSIGN(GetFileInfoWorker); | 350 DISALLOW_COPY_AND_ASSIGN(GetFileInfoWorker); |
| 347 }; | 351 }; |
| 348 | 352 |
| 349 // Worker class to read media device file data given a file |path|. | 353 // Worker class to read media device file data given a file |path|. |
| 350 class ReadFileWorker | 354 class ReadFileWorker |
| 351 : public RefCountedThreadSafe<ReadFileWorker, ReadFileWorkerDeleter> { | 355 : public RefCountedThreadSafe<ReadFileWorker, ReadFileWorkerDeleter> { |
| 352 public: | 356 public: |
| 353 // Constructed on |media_task_runner_| thread. | 357 // Constructed on |media_task_runner_| thread. |
| 354 ReadFileWorker(const std::string& handle, | 358 ReadFileWorker(const std::string& handle, |
| 355 const std::string& path, | 359 const std::string& src_path, |
| 356 uint32 total_size, | 360 uint32 total_size, |
| 361 const FilePath& dest_path, | |
| 357 SequencedTaskRunner* task_runner, | 362 SequencedTaskRunner* task_runner, |
| 358 WaitableEvent* task_completed_event, | 363 WaitableEvent* task_completed_event, |
| 359 WaitableEvent* shutdown_event) | 364 WaitableEvent* shutdown_event) |
| 360 : device_handle_(handle), | 365 : device_handle_(handle), |
| 361 path_(path), | 366 src_path_(src_path), |
| 362 total_bytes_(total_size), | 367 total_bytes_(total_size), |
| 368 dest_path_(dest_path), | |
| 369 dest_fd_(-1), | |
| 370 bytes_read_(0), | |
| 363 error_occurred_(false), | 371 error_occurred_(false), |
| 364 media_task_runner_(task_runner), | 372 media_task_runner_(task_runner), |
| 365 on_task_completed_event_(task_completed_event), | 373 on_task_completed_event_(task_completed_event), |
| 366 on_shutdown_event_(shutdown_event) { | 374 on_shutdown_event_(shutdown_event) { |
| 367 DCHECK(on_task_completed_event_); | 375 DCHECK(on_task_completed_event_); |
| 368 DCHECK(on_shutdown_event_); | 376 DCHECK(on_shutdown_event_); |
| 369 } | 377 } |
| 370 | 378 |
| 371 // This function is invoked on |media_task_runner_| to post the task on UI | 379 // This function is invoked on |media_task_runner_| to post the task on UI |
| 372 // thread. This blocks the |media_task_runner_| until the task is complete. | 380 // thread. This blocks the |media_task_runner_| until the task is complete. |
| 373 void Run() { | 381 void Run() { |
| 374 if (on_shutdown_event_->IsSignaled()) { | 382 if (on_shutdown_event_->IsSignaled()) { |
| 375 // Process is in shutdown mode. | 383 // Process is in shutdown mode. |
| 376 // Do not post any task on |media_task_runner_|. | 384 // Do not post any task on |media_task_runner_|. |
| 377 return; | 385 return; |
| 378 } | 386 } |
| 379 | 387 |
| 380 while (!error_occurred_ && (data_.size() < total_bytes_) && | 388 dest_fd_ = open(dest_path_.value().c_str(), O_WRONLY); |
| 381 !cancel_tasks_flag_.IsSet()) { | 389 if (dest_fd_ < 0) |
| 390 return; | |
| 391 dest_fd_scoper_.reset(&dest_fd_); | |
| 392 | |
| 393 while (!error_occurred_ && (bytes_read_ < total_bytes_)) { | |
| 382 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 394 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| 383 Bind(&ReadFileWorker::DoWorkOnUIThread, this)); | 395 Bind(&ReadFileWorker::DoWorkOnUIThread, this)); |
| 384 on_task_completed_event_->Wait(); | 396 on_task_completed_event_->Wait(); |
| 385 if (on_shutdown_event_->IsSignaled()) | 397 if (on_shutdown_event_->IsSignaled()) { |
| 386 cancel_tasks_flag_.Set(); | 398 cancel_tasks_flag_.Set(); |
| 399 break; | |
| 400 } | |
|
kmadhusu
2012/11/30 02:05:12
When the MTR resumes, if |error_occured_| is true,
Lei Zhang
2012/11/30 02:30:55
Done.
| |
| 401 int bytes_written = | |
| 402 file_util::WriteFileDescriptor(dest_fd_, data_.data(), data_.size()); | |
| 403 if (static_cast<int>(data_.size()) != bytes_written) { | |
| 404 error_occurred_ = true; | |
| 405 break; | |
| 406 } | |
| 407 bytes_read_ += data_.size(); | |
| 387 } | 408 } |
| 388 } | 409 } |
| 389 | 410 |
| 390 // Returns the media file contents received from mtpd. | 411 bool Succeeded() const { |
| 391 const std::string& data() const { return data_; } | 412 return !error_occurred_ && bytes_read_ == total_bytes_; |
|
kmadhusu
2012/11/30 02:05:12
nit: () around binary expressions.
Lei Zhang
2012/11/30 02:30:55
Done.
| |
| 413 } | |
| 392 | 414 |
| 393 // Returns the |media_task_runner_| associated with this worker object. | 415 // Returns the |media_task_runner_| associated with this worker object. |
| 394 // This function is exposed for WorkerDeleter struct to access the | 416 // This function is exposed for WorkerDeleter struct to access the |
| 395 // |media_task_runner_|. | 417 // |media_task_runner_|. |
| 396 SequencedTaskRunner* media_task_runner() const { | 418 SequencedTaskRunner* media_task_runner() const { |
| 397 return media_task_runner_.get(); | 419 return media_task_runner_.get(); |
| 398 } | 420 } |
| 399 | 421 |
| 400 private: | 422 private: |
| 401 friend struct WorkerDeleter<ReadFileWorker>; | 423 friend struct WorkerDeleter<ReadFileWorker>; |
| 402 friend class DeleteHelper<ReadFileWorker>; | 424 friend class DeleteHelper<ReadFileWorker>; |
| 403 friend class RefCountedThreadSafe<ReadFileWorker, ReadFileWorkerDeleter>; | 425 friend class RefCountedThreadSafe<ReadFileWorker, ReadFileWorkerDeleter>; |
| 404 | 426 |
| 405 // Destructed via ReadFileWorkerDeleter. | 427 // Destructed via ReadFileWorkerDeleter. |
| 406 virtual ~ReadFileWorker() { | 428 virtual ~ReadFileWorker() { |
| 407 // This object must be destructed on |media_task_runner_|. | 429 // This object must be destructed on |media_task_runner_|. |
| 408 } | 430 } |
| 409 | 431 |
| 410 // Dispatches a request to MediaTransferProtocolManager to get the media file | 432 // Dispatches a request to MediaTransferProtocolManager to get the media file |
| 411 // contents. | 433 // contents. |
| 412 void DoWorkOnUIThread() { | 434 void DoWorkOnUIThread() { |
| 413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 435 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 414 if (cancel_tasks_flag_.IsSet()) | 436 if (cancel_tasks_flag_.IsSet()) |
| 415 return; | 437 return; |
| 416 | 438 |
| 417 GetMediaTransferProtocolManager()->ReadFileChunkByPath( | 439 GetMediaTransferProtocolManager()->ReadFileChunkByPath( |
| 418 device_handle_, path_, data_.size(), BytesToRead(), | 440 device_handle_, src_path_, bytes_read_, BytesToRead(), |
| 419 Bind(&ReadFileWorker::OnDidWorkOnUIThread, this)); | 441 Bind(&ReadFileWorker::OnDidWorkOnUIThread, this)); |
| 420 } | 442 } |
| 421 | 443 |
| 422 // Query callback for DoWorkOnUIThread(). On success, |data| has the media | 444 // Query callback for DoWorkOnUIThread(). On success, |data| has the media |
| 423 // file contents. On failure, |error| is set to true. This function signals | 445 // file contents. On failure, |error| is set to true. This function signals |
| 424 // to unblock |media_task_runner_|. | 446 // to unblock |media_task_runner_|. |
| 425 void OnDidWorkOnUIThread(const std::string& data, bool error) { | 447 void OnDidWorkOnUIThread(const std::string& data, bool error) { |
| 426 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 448 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 427 if (cancel_tasks_flag_.IsSet()) | 449 if (cancel_tasks_flag_.IsSet()) |
| 428 return; | 450 return; |
| 429 | 451 |
| 430 error_occurred_ = error; | 452 error_occurred_ = error || (data.size() != BytesToRead()); |
| 431 if (!error) { | 453 if (!error_occurred_) |
|
kmadhusu
2012/11/30 02:05:12
If |error_occurred_| is true, we should reset |dat
Lei Zhang
2012/11/30 02:30:55
I fixed the checks above so that won't happen.
| |
| 432 if ((BytesToRead() == data.size())) { | 454 data_ = data; |
| 433 // TODO(kmadhusu): Data could be really huge. Consider passing data by | |
| 434 // pointer/ref rather than by value here to avoid an extra data copy. | |
| 435 data_.append(data); | |
| 436 } else { | |
| 437 NOTREACHED(); | |
| 438 error_occurred_ = true; | |
| 439 } | |
| 440 } | |
| 441 on_task_completed_event_->Signal(); | 455 on_task_completed_event_->Signal(); |
| 442 } | 456 } |
| 443 | 457 |
| 444 uint32 BytesToRead() const { | 458 uint32 BytesToRead() const { |
| 445 // Read data in 1 MB chunks. | 459 // Read data in 1 MB chunks. |
| 446 static const uint32 kReadChunkSize = 1024 * 1024; | 460 static const uint32 kReadChunkSize = 1024 * 1024; |
| 447 return std::min(kReadChunkSize, | 461 return std::min(kReadChunkSize, total_bytes_ - bytes_read_); |
| 448 total_bytes_ - static_cast<uint32>(data_.size())); | |
| 449 } | 462 } |
| 450 | 463 |
| 451 // The device unique identifier to query the device. | 464 // The device unique identifier to query the device. |
| 452 const std::string device_handle_; | 465 const std::string device_handle_; |
| 453 | 466 |
| 454 // The media device file path. | 467 // The media device file path. |
| 455 const std::string path_; | 468 const std::string src_path_; |
| 456 | |
| 457 // The data from mtpd. | |
| 458 std::string data_; | |
| 459 | 469 |
| 460 // Number of bytes to read. | 470 // Number of bytes to read. |
| 461 const uint32 total_bytes_; | 471 const uint32 total_bytes_; |
| 462 | 472 |
| 473 // Where to write the data read from the device. | |
| 474 const FilePath dest_path_; | |
| 475 | |
| 476 // File descriptor for |dest_path_|. | |
| 477 int dest_fd_; | |
| 478 | |
| 479 // Scoper to cleanup |dest_fd_|. | |
| 480 file_util::ScopedFD dest_fd_scoper_; | |
|
kmadhusu
2012/11/30 02:05:12
Is there any specific reason to have |dest_fd_| an
Lei Zhang
2012/11/30 02:30:55
Not needed any more. Removed.
| |
| 481 | |
| 482 /***************************************************************************** | |
| 483 * The variables below are accessed on both |media_task_runner_| and the UI | |
| 484 * thread. However, there's no concurrent access because the UI thread is in a | |
| 485 * blocked state when access occurs on |media_task_runner_|. | |
| 486 */ | |
| 487 | |
| 488 // Number of bytes read from the device. | |
| 489 uint32 bytes_read_; | |
| 490 | |
| 491 // Temporary data storage. | |
| 492 std::string data_; | |
| 493 | |
| 463 // Whether an error occurred during file transfer. | 494 // Whether an error occurred during file transfer. |
| 464 bool error_occurred_; | 495 bool error_occurred_; |
| 465 | 496 |
| 497 /****************************************************************************/ | |
| 498 | |
| 466 // A reference to |media_task_runner_| to destruct this object on the correct | 499 // A reference to |media_task_runner_| to destruct this object on the correct |
| 467 // thread. | 500 // thread. |
| 468 scoped_refptr<SequencedTaskRunner> media_task_runner_; | 501 scoped_refptr<SequencedTaskRunner> media_task_runner_; |
| 469 | 502 |
| 470 // |media_task_runner_| can wait on this event until the required operation | 503 // |media_task_runner_| can wait on this event until the required operation |
| 471 // is complete. | 504 // is complete. |
| 472 // TODO(kmadhusu): Remove this WaitableEvent after modifying the | 505 // TODO(kmadhusu): Remove this WaitableEvent after modifying the |
| 473 // DeviceMediaFileUtil functions as asynchronous functions. | 506 // DeviceMediaFileUtil functions as asynchronous functions. |
| 474 WaitableEvent* on_task_completed_event_; | 507 WaitableEvent* on_task_completed_event_; |
| 475 | 508 |
| (...skipping 377 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 853 PlatformFileError error = GetFileInfo(device_file_path, file_info); | 886 PlatformFileError error = GetFileInfo(device_file_path, file_info); |
| 854 if (error != base::PLATFORM_FILE_OK) | 887 if (error != base::PLATFORM_FILE_OK) |
| 855 return error; | 888 return error; |
| 856 | 889 |
| 857 if (file_info->size <= 0 || file_info->size > kuint32max) | 890 if (file_info->size <= 0 || file_info->size > kuint32max) |
| 858 return base::PLATFORM_FILE_ERROR_FAILED; | 891 return base::PLATFORM_FILE_ERROR_FAILED; |
| 859 | 892 |
| 860 scoped_refptr<ReadFileWorker> worker(new ReadFileWorker( | 893 scoped_refptr<ReadFileWorker> worker(new ReadFileWorker( |
| 861 device_handle_, | 894 device_handle_, |
| 862 GetDeviceRelativePath(device_path_, device_file_path.value()), | 895 GetDeviceRelativePath(device_path_, device_file_path.value()), |
| 863 file_info->size, | 896 file_info->size, local_path, |
| 864 media_task_runner_, &on_task_completed_event_, &on_shutdown_event_)); | 897 media_task_runner_, &on_task_completed_event_, &on_shutdown_event_)); |
|
kmadhusu
2012/11/30 02:05:12
nit: some of these params can fit in the previous
Lei Zhang
2012/11/30 02:30:55
Done.
That could have been said of the previous co
| |
| 865 worker->Run(); | 898 worker->Run(); |
| 866 | 899 |
| 867 const std::string& file_data = worker->data(); | 900 if (!worker->Succeeded()) |
| 868 int data_size = static_cast<int>(file_data.length()); | |
| 869 if (file_data.empty() || | |
| 870 file_util::WriteFile(local_path, file_data.c_str(), | |
| 871 data_size) != data_size) { | |
| 872 return base::PLATFORM_FILE_ERROR_FAILED; | 901 return base::PLATFORM_FILE_ERROR_FAILED; |
| 873 } | |
| 874 | 902 |
| 875 // Modify the last modified time to null. This prevents the time stamp | 903 // Modify the last modified time to null. This prevents the time stamp |
| 876 // verfication in LocalFileStreamReader. | 904 // verfication in LocalFileStreamReader. |
| 877 file_info->last_modified = base::Time(); | 905 file_info->last_modified = base::Time(); |
| 878 return error; | 906 return base::PLATFORM_FILE_OK; |
| 879 } | 907 } |
| 880 | 908 |
| 881 SequencedTaskRunner* MTPDeviceDelegateImplLinux::GetMediaTaskRunner() { | 909 SequencedTaskRunner* MTPDeviceDelegateImplLinux::GetMediaTaskRunner() { |
| 882 return media_task_runner_.get(); | 910 return media_task_runner_.get(); |
| 883 } | 911 } |
| 884 | 912 |
| 885 void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() { | 913 void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() { |
| 886 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 914 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 887 // Caution: This function is called on the IO thread. Access only the thread | 915 // Caution: This function is called on the IO thread. Access only the thread |
| 888 // safe member variables in this function. Do all the clean up operations in | 916 // safe member variables in this function. Do all the clean up operations in |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 929 } | 957 } |
| 930 | 958 |
| 931 void MTPDeviceDelegateImplLinux::DeleteDelegateOnTaskRunner() { | 959 void MTPDeviceDelegateImplLinux::DeleteDelegateOnTaskRunner() { |
| 932 DCHECK(media_task_runner_->RunsTasksOnCurrentThread()); | 960 DCHECK(media_task_runner_->RunsTasksOnCurrentThread()); |
| 933 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 961 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| 934 Bind(&CloseStorageOnUIThread, device_handle_)); | 962 Bind(&CloseStorageOnUIThread, device_handle_)); |
| 935 delete this; | 963 delete this; |
| 936 } | 964 } |
| 937 | 965 |
| 938 } // namespace chrome | 966 } // namespace chrome |
| OLD | NEW |