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