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