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