Home | History | Annotate | Download | only in linux
      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_galleries/linux/mtp_device_delegate_impl_linux.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/files/file_path.h"
      9 #include "base/strings/string_util.h"
     10 #include "chrome/browser/media_galleries/linux/mtp_device_task_helper.h"
     11 #include "chrome/browser/media_galleries/linux/mtp_device_task_helper_map_service.h"
     12 #include "chrome/browser/media_galleries/linux/snapshot_file_details.h"
     13 #include "content/public/browser/browser_thread.h"
     14 
     15 namespace chrome {
     16 
     17 namespace {
     18 
     19 // File path separator constant.
     20 const char kRootPath[] = "/";
     21 
     22 // Returns the device relative file path given |file_path|.
     23 // E.g.: If the |file_path| is "/usb:2,2:12345/DCIM" and |registered_dev_path|
     24 // is "/usb:2,2:12345", this function returns the device relative path which is
     25 // "/DCIM".
     26 std::string GetDeviceRelativePath(const base::FilePath& registered_dev_path,
     27                                   const base::FilePath& file_path) {
     28   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
     29   DCHECK(!registered_dev_path.empty());
     30   DCHECK(!file_path.empty());
     31   if (registered_dev_path == file_path)
     32     return kRootPath;
     33 
     34   base::FilePath relative_path;
     35   if (!registered_dev_path.AppendRelativePath(file_path, &relative_path))
     36     return std::string();
     37   DCHECK(!relative_path.empty());
     38   return relative_path.value();
     39 }
     40 
     41 // Returns the MTPDeviceTaskHelper object associated with the MTP device
     42 // storage.
     43 //
     44 // |storage_name| specifies the name of the storage device.
     45 // Returns NULL if the |storage_name| is no longer valid (e.g. because the
     46 // corresponding storage device is detached, etc).
     47 MTPDeviceTaskHelper* GetDeviceTaskHelperForStorage(
     48     const std::string& storage_name) {
     49   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     50   return MTPDeviceTaskHelperMapService::GetInstance()->GetDeviceTaskHelper(
     51       storage_name);
     52 }
     53 
     54 // Opens the storage device for communication.
     55 //
     56 // Called on the UI thread to dispatch the request to the
     57 // MediaTransferProtocolManager.
     58 //
     59 // |storage_name| specifies the name of the storage device.
     60 // |reply_callback| is called when the OpenStorage request completes.
     61 // |reply_callback| runs on the IO thread.
     62 void OpenStorageOnUIThread(
     63     const std::string& storage_name,
     64     const base::Callback<void(bool)>& reply_callback) {
     65   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     66   MTPDeviceTaskHelper* task_helper =
     67       GetDeviceTaskHelperForStorage(storage_name);
     68   if (!task_helper) {
     69     task_helper =
     70         MTPDeviceTaskHelperMapService::GetInstance()->CreateDeviceTaskHelper(
     71             storage_name);
     72   }
     73   task_helper->OpenStorage(storage_name, reply_callback);
     74 }
     75 
     76 // Enumerates the |root| directory file entries.
     77 //
     78 // Called on the UI thread to dispatch the request to the
     79 // MediaTransferProtocolManager.
     80 //
     81 // |storage_name| specifies the name of the storage device.
     82 // |success_callback| is called when the ReadDirectory request succeeds.
     83 // |error_callback| is called when the ReadDirectory request fails.
     84 // |success_callback| and |error_callback| runs on the IO thread.
     85 void ReadDirectoryOnUIThread(
     86     const std::string& storage_name,
     87     const std::string& root,
     88     const base::Callback<
     89         void(const fileapi::AsyncFileUtil::EntryList&)>& success_callback,
     90     const base::Callback<void(base::PlatformFileError)>& error_callback) {
     91   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     92   MTPDeviceTaskHelper* task_helper =
     93       GetDeviceTaskHelperForStorage(storage_name);
     94   if (!task_helper)
     95     return;
     96   task_helper->ReadDirectoryByPath(root, success_callback, error_callback);
     97 }
     98 
     99 // Gets the |file_path| details.
    100 //
    101 // Called on the UI thread to dispatch the request to the
    102 // MediaTransferProtocolManager.
    103 //
    104 // |storage_name| specifies the name of the storage device.
    105 // |success_callback| is called when the GetFileInfo request succeeds.
    106 // |error_callback| is called when the GetFileInfo request fails.
    107 // |success_callback| and |error_callback| runs on the IO thread.
    108 void GetFileInfoOnUIThread(
    109     const std::string& storage_name,
    110     const std::string& file_path,
    111     const base::Callback<void(const base::PlatformFileInfo&)>& success_callback,
    112     const base::Callback<void(base::PlatformFileError)>& error_callback) {
    113   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    114   MTPDeviceTaskHelper* task_helper =
    115       GetDeviceTaskHelperForStorage(storage_name);
    116   if (!task_helper)
    117     return;
    118   task_helper->GetFileInfoByPath(file_path, success_callback, error_callback);
    119 }
    120 
    121 // Copies the contents of |device_file_path| to |snapshot_file_path|.
    122 //
    123 // Called on the UI thread to dispatch the request to the
    124 // MediaTransferProtocolManager.
    125 //
    126 // |storage_name| specifies the name of the storage device.
    127 // |device_file_path| specifies the media device file path.
    128 // |snapshot_file_path| specifies the platform path of the snapshot file.
    129 // |file_size| specifies the number of bytes that will be written to the
    130 // snapshot file.
    131 // |success_callback| is called when the copy operation succeeds.
    132 // |error_callback| is called when the copy operation fails.
    133 // |success_callback| and |error_callback| runs on the IO thread.
    134 void WriteDataIntoSnapshotFileOnUIThread(
    135     const std::string& storage_name,
    136     const SnapshotRequestInfo& request_info,
    137     const base::PlatformFileInfo& snapshot_file_info) {
    138   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    139   MTPDeviceTaskHelper* task_helper =
    140       GetDeviceTaskHelperForStorage(storage_name);
    141   if (!task_helper)
    142     return;
    143   task_helper->WriteDataIntoSnapshotFile(request_info, snapshot_file_info);
    144 }
    145 
    146 // Closes the device storage specified by the |storage_name| and destroys the
    147 // MTPDeviceTaskHelper object associated with the device storage.
    148 //
    149 // Called on the UI thread to dispatch the request to the
    150 // MediaTransferProtocolManager.
    151 void CloseStorageAndDestroyTaskHelperOnUIThread(
    152     const std::string& storage_name) {
    153   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    154   MTPDeviceTaskHelper* task_helper =
    155       GetDeviceTaskHelperForStorage(storage_name);
    156   if (!task_helper)
    157     return;
    158   task_helper->CloseStorage();
    159   MTPDeviceTaskHelperMapService::GetInstance()->DestroyDeviceTaskHelper(
    160       storage_name);
    161 }
    162 
    163 }  // namespace
    164 
    165 MTPDeviceDelegateImplLinux::PendingTaskInfo::PendingTaskInfo(
    166     const tracked_objects::Location& location,
    167     const base::Closure& task)
    168     : location(location),
    169       task(task) {
    170 }
    171 
    172 MTPDeviceDelegateImplLinux::PendingTaskInfo::~PendingTaskInfo() {
    173 }
    174 
    175 MTPDeviceDelegateImplLinux::MTPDeviceDelegateImplLinux(
    176     const std::string& device_location)
    177     : init_state_(UNINITIALIZED),
    178       task_in_progress_(false),
    179       device_path_(device_location),
    180       weak_ptr_factory_(this) {
    181   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    182   DCHECK(!device_path_.empty());
    183   RemoveChars(device_location, kRootPath, &storage_name_);
    184   DCHECK(!storage_name_.empty());
    185 }
    186 
    187 MTPDeviceDelegateImplLinux::~MTPDeviceDelegateImplLinux() {
    188   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    189 }
    190 
    191 void MTPDeviceDelegateImplLinux::GetFileInfo(
    192     const base::FilePath& file_path,
    193     const GetFileInfoSuccessCallback& success_callback,
    194     const ErrorCallback& error_callback) {
    195   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    196   DCHECK(!file_path.empty());
    197   base::Closure call_closure =
    198       base::Bind(&GetFileInfoOnUIThread,
    199                  storage_name_,
    200                  GetDeviceRelativePath(device_path_, file_path),
    201                  base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfo,
    202                             weak_ptr_factory_.GetWeakPtr(),
    203                             success_callback),
    204                  base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
    205                             weak_ptr_factory_.GetWeakPtr(),
    206                             error_callback));
    207   EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE, call_closure));
    208 }
    209 
    210 void MTPDeviceDelegateImplLinux::ReadDirectory(
    211     const base::FilePath& root,
    212     const ReadDirectorySuccessCallback& success_callback,
    213     const ErrorCallback& error_callback) {
    214   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    215   DCHECK(!root.empty());
    216   std::string device_file_relative_path = GetDeviceRelativePath(device_path_,
    217                                                                 root);
    218   base::Closure call_closure =
    219       base::Bind(
    220           &GetFileInfoOnUIThread,
    221           storage_name_,
    222           device_file_relative_path,
    223           base::Bind(
    224               &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory,
    225               weak_ptr_factory_.GetWeakPtr(),
    226               device_file_relative_path,
    227               success_callback,
    228               error_callback),
    229           base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
    230                      weak_ptr_factory_.GetWeakPtr(),
    231                      error_callback));
    232   EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE, call_closure));
    233 }
    234 
    235 void MTPDeviceDelegateImplLinux::CreateSnapshotFile(
    236     const base::FilePath& device_file_path,
    237     const base::FilePath& snapshot_file_path,
    238     const CreateSnapshotFileSuccessCallback& success_callback,
    239     const ErrorCallback& error_callback) {
    240   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    241   DCHECK(!device_file_path.empty());
    242   DCHECK(!snapshot_file_path.empty());
    243   std::string device_file_relative_path =
    244       GetDeviceRelativePath(device_path_, device_file_path);
    245   scoped_ptr<SnapshotRequestInfo> request_info(
    246       new SnapshotRequestInfo(device_file_relative_path,
    247                               snapshot_file_path,
    248                               success_callback,
    249                               error_callback));
    250   base::Closure call_closure =
    251       base::Bind(
    252           &GetFileInfoOnUIThread,
    253           storage_name_,
    254           device_file_relative_path,
    255           base::Bind(
    256               &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile,
    257               weak_ptr_factory_.GetWeakPtr(),
    258               base::Passed(&request_info)),
    259           base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
    260                      weak_ptr_factory_.GetWeakPtr(),
    261                      error_callback));
    262   EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE, call_closure));
    263 }
    264 
    265 void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() {
    266   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    267   // To cancel all the pending tasks, destroy the MTPDeviceTaskHelper object.
    268   content::BrowserThread::PostTask(
    269       content::BrowserThread::UI,
    270       FROM_HERE,
    271       base::Bind(&CloseStorageAndDestroyTaskHelperOnUIThread, storage_name_));
    272   delete this;
    273 }
    274 
    275 void MTPDeviceDelegateImplLinux::EnsureInitAndRunTask(
    276     const PendingTaskInfo& task_info) {
    277   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    278   if ((init_state_ == INITIALIZED) && !task_in_progress_) {
    279     task_in_progress_ = true;
    280     content::BrowserThread::PostTask(content::BrowserThread::UI,
    281                                      task_info.location,
    282                                      task_info.task);
    283     return;
    284   }
    285   pending_tasks_.push(task_info);
    286   if (init_state_ == UNINITIALIZED) {
    287     init_state_ = PENDING_INIT;
    288     task_in_progress_ = true;
    289     content::BrowserThread::PostTask(
    290         content::BrowserThread::UI,
    291         FROM_HERE,
    292         base::Bind(&OpenStorageOnUIThread,
    293                    storage_name_,
    294                    base::Bind(&MTPDeviceDelegateImplLinux::OnInitCompleted,
    295                               weak_ptr_factory_.GetWeakPtr())));
    296   }
    297 }
    298 
    299 void MTPDeviceDelegateImplLinux::WriteDataIntoSnapshotFile(
    300     const base::PlatformFileInfo& file_info) {
    301   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    302   DCHECK(current_snapshot_request_info_.get());
    303   DCHECK_GT(file_info.size, 0);
    304   task_in_progress_ = true;
    305   SnapshotRequestInfo request_info(
    306       current_snapshot_request_info_->device_file_path,
    307       current_snapshot_request_info_->snapshot_file_path,
    308       base::Bind(
    309           &MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile,
    310           weak_ptr_factory_.GetWeakPtr()),
    311       base::Bind(
    312           &MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError,
    313           weak_ptr_factory_.GetWeakPtr()));
    314 
    315   base::Closure task_closure = base::Bind(&WriteDataIntoSnapshotFileOnUIThread,
    316                                           storage_name_,
    317                                           request_info,
    318                                           file_info);
    319   content::BrowserThread::PostTask(content::BrowserThread::UI,
    320                                    FROM_HERE,
    321                                    task_closure);
    322 }
    323 
    324 void MTPDeviceDelegateImplLinux::ProcessNextPendingRequest() {
    325   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    326   DCHECK(!task_in_progress_);
    327   if (pending_tasks_.empty())
    328     return;
    329 
    330   task_in_progress_ = true;
    331   const PendingTaskInfo& task_info = pending_tasks_.front();
    332   content::BrowserThread::PostTask(content::BrowserThread::UI,
    333                                    task_info.location,
    334                                    task_info.task);
    335   pending_tasks_.pop();
    336 }
    337 
    338 void MTPDeviceDelegateImplLinux::OnInitCompleted(bool succeeded) {
    339   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    340   init_state_ = succeeded ? INITIALIZED : UNINITIALIZED;
    341   task_in_progress_ = false;
    342   ProcessNextPendingRequest();
    343 }
    344 
    345 void MTPDeviceDelegateImplLinux::OnDidGetFileInfo(
    346     const GetFileInfoSuccessCallback& success_callback,
    347     const base::PlatformFileInfo& file_info) {
    348   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    349   success_callback.Run(file_info);
    350   task_in_progress_ = false;
    351   ProcessNextPendingRequest();
    352 }
    353 
    354 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory(
    355     const std::string& root,
    356     const ReadDirectorySuccessCallback& success_callback,
    357     const ErrorCallback& error_callback,
    358     const base::PlatformFileInfo& file_info) {
    359   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    360   DCHECK(task_in_progress_);
    361   if (!file_info.is_directory) {
    362     return HandleDeviceFileError(error_callback,
    363                                  base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY);
    364   }
    365 
    366   base::Closure task_closure =
    367       base::Bind(&ReadDirectoryOnUIThread,
    368                  storage_name_,
    369                  root,
    370                  base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadDirectory,
    371                             weak_ptr_factory_.GetWeakPtr(),
    372                             success_callback),
    373                  base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
    374                             weak_ptr_factory_.GetWeakPtr(),
    375                             error_callback));
    376   content::BrowserThread::PostTask(content::BrowserThread::UI,
    377                                    FROM_HERE,
    378                                    task_closure);
    379 }
    380 
    381 void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile(
    382     scoped_ptr<SnapshotRequestInfo> snapshot_request_info,
    383     const base::PlatformFileInfo& file_info) {
    384   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    385   DCHECK(!current_snapshot_request_info_.get());
    386   DCHECK(snapshot_request_info.get());
    387   DCHECK(task_in_progress_);
    388   base::PlatformFileError error = base::PLATFORM_FILE_OK;
    389   if (file_info.is_directory)
    390     error = base::PLATFORM_FILE_ERROR_NOT_A_FILE;
    391   else if (file_info.size < 0 || file_info.size > kuint32max)
    392     error = base::PLATFORM_FILE_ERROR_FAILED;
    393 
    394   if (error != base::PLATFORM_FILE_OK)
    395     return HandleDeviceFileError(snapshot_request_info->error_callback, error);
    396 
    397   base::PlatformFileInfo snapshot_file_info(file_info);
    398   // Modify the last modified time to null. This prevents the time stamp
    399   // verfication in LocalFileStreamReader.
    400   snapshot_file_info.last_modified = base::Time();
    401 
    402   current_snapshot_request_info_.reset(snapshot_request_info.release());
    403   if (file_info.size == 0) {
    404     // Empty snapshot file.
    405     return OnDidWriteDataIntoSnapshotFile(
    406         snapshot_file_info, current_snapshot_request_info_->snapshot_file_path);
    407   }
    408   WriteDataIntoSnapshotFile(snapshot_file_info);
    409 }
    410 
    411 void MTPDeviceDelegateImplLinux::OnDidReadDirectory(
    412     const ReadDirectorySuccessCallback& success_callback,
    413     const fileapi::AsyncFileUtil::EntryList& file_list) {
    414   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    415   success_callback.Run(file_list, false /*no more entries*/);
    416   task_in_progress_ = false;
    417   ProcessNextPendingRequest();
    418 }
    419 
    420 void MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile(
    421     const base::PlatformFileInfo& file_info,
    422     const base::FilePath& snapshot_file_path) {
    423   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    424   DCHECK(current_snapshot_request_info_.get());
    425   DCHECK(task_in_progress_);
    426   current_snapshot_request_info_->success_callback.Run(
    427       file_info, snapshot_file_path);
    428   task_in_progress_ = false;
    429   current_snapshot_request_info_.reset();
    430   ProcessNextPendingRequest();
    431 }
    432 
    433 void MTPDeviceDelegateImplLinux::OnWriteDataIntoSnapshotFileError(
    434     base::PlatformFileError error) {
    435   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    436   DCHECK(current_snapshot_request_info_.get());
    437   DCHECK(task_in_progress_);
    438   current_snapshot_request_info_->error_callback.Run(error);
    439   task_in_progress_ = false;
    440   current_snapshot_request_info_.reset();
    441   ProcessNextPendingRequest();
    442 }
    443 
    444 void MTPDeviceDelegateImplLinux::HandleDeviceFileError(
    445     const ErrorCallback& error_callback,
    446     base::PlatformFileError error) {
    447   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    448   error_callback.Run(error);
    449   task_in_progress_ = false;
    450   ProcessNextPendingRequest();
    451 }
    452 
    453 void CreateMTPDeviceAsyncDelegate(
    454     const std::string& device_location,
    455     const CreateMTPDeviceAsyncDelegateCallback& callback) {
    456   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    457   callback.Run(new MTPDeviceDelegateImplLinux(device_location));
    458 }
    459 
    460 }  // namespace chrome
    461