1 // Copyright (c) 2013 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 // MTPDeviceDelegateImplWin implementation.
6 
7 #include "chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.h"
8 
9 #include <portabledevice.h>
10 #include <stddef.h>
11 
12 #include <utility>
13 #include <vector>
14 
15 #include "base/bind.h"
16 #include "base/check_op.h"
17 #include "base/files/file_path.h"
18 #include "base/memory/ref_counted.h"
19 #include "base/notreached.h"
20 #include "base/sequenced_task_runner.h"
21 #include "base/stl_util.h"
22 #include "base/strings/string_split.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/task/post_task.h"
26 #include "base/task_runner_util.h"
27 #include "base/threading/scoped_blocking_call.h"
28 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
29 #include "chrome/browser/media_galleries/win/mtp_device_object_entry.h"
30 #include "chrome/browser/media_galleries/win/mtp_device_object_enumerator.h"
31 #include "chrome/browser/media_galleries/win/mtp_device_operations_util.h"
32 #include "chrome/browser/media_galleries/win/portable_device_map_service.h"
33 #include "chrome/browser/media_galleries/win/snapshot_file_details.h"
34 #include "components/services/filesystem/public/mojom/types.mojom.h"
35 #include "components/storage_monitor/storage_monitor.h"
36 #include "content/public/browser/browser_task_traits.h"
37 #include "content/public/browser/browser_thread.h"
38 #include "storage/common/file_system/file_system_util.h"
39 
40 namespace {
41 
42 // Gets the details of the MTP partition storage specified by the
43 // |storage_path| on the UI thread. Returns true if the storage details are
44 // valid and returns false otherwise.
GetStorageInfoOnUIThread(const base::string16 & storage_path,base::string16 * pnp_device_id,base::string16 * storage_object_id)45 bool GetStorageInfoOnUIThread(const base::string16& storage_path,
46                               base::string16* pnp_device_id,
47                               base::string16* storage_object_id) {
48   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
49   DCHECK(!storage_path.empty());
50   DCHECK(pnp_device_id);
51   DCHECK(storage_object_id);
52   base::string16 storage_device_id;
53   base::RemoveChars(storage_path, L"\\\\", &storage_device_id);
54   DCHECK(!storage_device_id.empty());
55   // TODO(gbillock): Take the StorageMonitor as an argument.
56   storage_monitor::StorageMonitor* monitor =
57       storage_monitor::StorageMonitor::GetInstance();
58   DCHECK(monitor);
59   return monitor->GetMTPStorageInfoFromDeviceId(
60       base::UTF16ToUTF8(storage_device_id), pnp_device_id, storage_object_id);
61 }
62 
63 // Returns the object id of the file object specified by the |file_path|,
64 // e.g. if the |file_path| is "\\MTP:StorageSerial:SID-{1001,,192}:125\DCIM"
65 // and |device_info.registered_device_path_| is
66 // "\\MTP:StorageSerial:SID-{1001,,192}:125", this function returns the
67 // identifier of the "DCIM" folder object.
68 //
69 // Returns an empty string if the device is detached while the request is in
70 // progress or when the |file_path| is invalid.
GetFileObjectIdFromPathOnBlockingPoolThread(const MTPDeviceDelegateImplWin::StorageDeviceInfo & device_info,const base::FilePath & file_path)71 base::string16 GetFileObjectIdFromPathOnBlockingPoolThread(
72     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
73     const base::FilePath& file_path) {
74   DCHECK(!file_path.empty());
75   IPortableDevice* device =
76       PortableDeviceMapService::GetInstance()->GetPortableDevice(
77           device_info.registered_device_path);
78   if (!device)
79     return base::string16();
80 
81   if (device_info.registered_device_path == file_path.value())
82     return device_info.storage_object_id;
83 
84   base::FilePath relative_path;
85   if (!base::FilePath(device_info.registered_device_path).AppendRelativePath(
86           file_path, &relative_path))
87     return base::string16();
88 
89   std::vector<base::string16> path_components;
90   relative_path.GetComponents(&path_components);
91   DCHECK(!path_components.empty());
92   base::string16 parent_id(device_info.storage_object_id);
93   base::string16 file_object_id;
94   for (size_t i = 0; i < path_components.size(); ++i) {
95     file_object_id =
96         media_transfer_protocol::GetObjectIdFromName(device, parent_id,
97                                                      path_components[i]);
98     if (file_object_id.empty())
99       break;
100     parent_id = file_object_id;
101   }
102   return file_object_id;
103 }
104 
105 // Returns a pointer to a new instance of AbstractFileEnumerator for the given
106 // |root| directory. Called on a blocking pool thread.
107 std::unique_ptr<MTPDeviceObjectEnumerator>
CreateFileEnumeratorOnBlockingPoolThread(const MTPDeviceDelegateImplWin::StorageDeviceInfo & device_info,const base::FilePath & root)108 CreateFileEnumeratorOnBlockingPoolThread(
109     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
110     const base::FilePath& root) {
111   DCHECK(!device_info.registered_device_path.empty());
112   DCHECK(!root.empty());
113   IPortableDevice* device =
114       PortableDeviceMapService::GetInstance()->GetPortableDevice(
115           device_info.registered_device_path);
116   if (!device)
117     return std::unique_ptr<MTPDeviceObjectEnumerator>();
118 
119   base::string16 object_id =
120       GetFileObjectIdFromPathOnBlockingPoolThread(device_info, root);
121   if (object_id.empty())
122     return std::unique_ptr<MTPDeviceObjectEnumerator>();
123 
124   MTPDeviceObjectEntries entries;
125   if (!media_transfer_protocol::GetDirectoryEntries(device, object_id,
126                                                     &entries) ||
127       entries.empty())
128     return std::unique_ptr<MTPDeviceObjectEnumerator>();
129 
130   return std::unique_ptr<MTPDeviceObjectEnumerator>(
131       new MTPDeviceObjectEnumerator(entries));
132 }
133 
134 // Opens the device for communication on a blocking pool thread.
135 // |pnp_device_id| specifies the PnP device id.
136 // |registered_device_path| specifies the registered file system root path for
137 // the given device.
OpenDeviceOnBlockingPoolThread(const base::string16 & pnp_device_id,const base::string16 & registered_device_path)138 bool OpenDeviceOnBlockingPoolThread(
139     const base::string16& pnp_device_id,
140     const base::string16& registered_device_path) {
141   DCHECK(!pnp_device_id.empty());
142   DCHECK(!registered_device_path.empty());
143   Microsoft::WRL::ComPtr<IPortableDevice> device =
144       media_transfer_protocol::OpenDevice(pnp_device_id);
145   bool init_succeeded = device.Get() != NULL;
146   if (init_succeeded) {
147     PortableDeviceMapService::GetInstance()->AddPortableDevice(
148         registered_device_path, device.Get());
149   }
150   return init_succeeded;
151 }
152 
153 // Gets the |file_path| details from the MTP device specified by the
154 // |device_info.registered_device_path|. On success, |error| is set to
155 // base::File::FILE_OK and fills in |file_info|. On failure, |error| is set
156 // to corresponding platform file error and |file_info| is not set.
GetFileInfoOnBlockingPoolThread(const MTPDeviceDelegateImplWin::StorageDeviceInfo & device_info,const base::FilePath & file_path,base::File::Info * file_info)157 base::File::Error GetFileInfoOnBlockingPoolThread(
158     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
159     const base::FilePath& file_path,
160     base::File::Info* file_info) {
161   DCHECK(!device_info.registered_device_path.empty());
162   DCHECK(!file_path.empty());
163   DCHECK(file_info);
164   IPortableDevice* device =
165       PortableDeviceMapService::GetInstance()->GetPortableDevice(
166           device_info.registered_device_path);
167   if (!device)
168     return base::File::FILE_ERROR_FAILED;
169 
170   base::string16 object_id =
171       GetFileObjectIdFromPathOnBlockingPoolThread(device_info, file_path);
172   if (object_id.empty())
173     return base::File::FILE_ERROR_FAILED;
174   return media_transfer_protocol::GetFileEntryInfo(device, object_id,
175                                                    file_info);
176 }
177 
178 // Reads the |root| directory file entries on a blocking pool thread. On
179 // success, |error| is set to base::File::FILE_OK and |entries| contains the
180 // directory file entries. On failure, |error| is set to platform file error
181 // and |entries| is not set.
ReadDirectoryOnBlockingPoolThread(const MTPDeviceDelegateImplWin::StorageDeviceInfo & device_info,const base::FilePath & root,storage::AsyncFileUtil::EntryList * entries)182 base::File::Error ReadDirectoryOnBlockingPoolThread(
183     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
184     const base::FilePath& root,
185     storage::AsyncFileUtil::EntryList* entries) {
186   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
187                                                 base::BlockingType::MAY_BLOCK);
188   DCHECK(!root.empty());
189   DCHECK(entries);
190   base::File::Info file_info;
191   base::File::Error error = GetFileInfoOnBlockingPoolThread(device_info, root,
192                                                             &file_info);
193   if (error != base::File::FILE_OK)
194     return error;
195 
196   if (!file_info.is_directory)
197     return base::File::FILE_ERROR_NOT_A_DIRECTORY;
198 
199   base::FilePath current;
200   std::unique_ptr<MTPDeviceObjectEnumerator> file_enum =
201       CreateFileEnumeratorOnBlockingPoolThread(device_info, root);
202   if (!file_enum)
203     return error;
204 
205   while (!(current = file_enum->Next()).empty()) {
206     entries->emplace_back(storage::VirtualPath::BaseName(current),
207                           file_enum->IsDirectory()
208                               ? filesystem::mojom::FsFileType::DIRECTORY
209                               : filesystem::mojom::FsFileType::REGULAR_FILE);
210   }
211   return error;
212 }
213 
214 // Gets the device file stream object on a blocking pool thread.
215 // |device_info| contains the device storage partition details.
216 // On success, returns base::File::FILE_OK and file stream details are set in
217 // |file_details|. On failure, returns a platform file error and file stream
218 // details are not set in |file_details|.
GetFileStreamOnBlockingPoolThread(const MTPDeviceDelegateImplWin::StorageDeviceInfo & device_info,SnapshotFileDetails * file_details)219 base::File::Error GetFileStreamOnBlockingPoolThread(
220     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
221     SnapshotFileDetails* file_details) {
222   DCHECK(file_details);
223   DCHECK(!file_details->request_info().device_file_path.empty());
224   DCHECK(!file_details->request_info().snapshot_file_path.empty());
225   IPortableDevice* device =
226       PortableDeviceMapService::GetInstance()->GetPortableDevice(
227           device_info.registered_device_path);
228   if (!device)
229     return base::File::FILE_ERROR_FAILED;
230 
231   base::string16 file_object_id =
232       GetFileObjectIdFromPathOnBlockingPoolThread(
233           device_info, file_details->request_info().device_file_path);
234   if (file_object_id.empty())
235     return base::File::FILE_ERROR_FAILED;
236 
237   base::File::Info file_info;
238   base::File::Error error =
239       GetFileInfoOnBlockingPoolThread(
240           device_info,
241           file_details->request_info().device_file_path,
242           &file_info);
243   if (error != base::File::FILE_OK)
244     return error;
245 
246   DWORD optimal_transfer_size = 0;
247   Microsoft::WRL::ComPtr<IStream> file_stream;
248   if (file_info.size > 0) {
249     HRESULT hr = media_transfer_protocol::GetFileStreamForObject(
250         device, file_object_id, &file_stream, &optimal_transfer_size);
251     if (hr != S_OK)
252       return base::File::FILE_ERROR_FAILED;
253   }
254 
255   // LocalFileStreamReader is used to read the contents of the snapshot file.
256   // Snapshot file modification time does not match the last modified time
257   // of the original media file. Therefore, set the last modified time to null
258   // in order to avoid the verification in LocalFileStreamReader.
259   //
260   // Users will use HTML5 FileSystem Entry getMetadata() interface to get the
261   // actual last modified time of the media file.
262   file_info.last_modified = base::Time();
263 
264   DCHECK(file_info.size == 0 || optimal_transfer_size > 0U);
265   file_details->set_file_info(file_info);
266   file_details->set_device_file_stream(file_stream.Get());
267   file_details->set_optimal_transfer_size(optimal_transfer_size);
268   return error;
269 }
270 
271 // Copies the data chunk from device file to the snapshot file based on the
272 // parameters specified by |file_details|.
273 // Returns the total number of bytes written to the snapshot file for non-empty
274 // files, or 0 on failure. For empty files, just return 0.
WriteDataChunkIntoSnapshotFileOnBlockingPoolThread(const SnapshotFileDetails & file_details)275 DWORD WriteDataChunkIntoSnapshotFileOnBlockingPoolThread(
276     const SnapshotFileDetails& file_details) {
277   if (file_details.file_info().size == 0)
278     return 0;
279   return media_transfer_protocol::CopyDataChunkToLocalFile(
280       file_details.device_file_stream(),
281       file_details.request_info().snapshot_file_path,
282       file_details.optimal_transfer_size());
283 }
284 
DeletePortableDeviceOnBlockingPoolThread(const base::string16 & registered_device_path)285 void DeletePortableDeviceOnBlockingPoolThread(
286     const base::string16& registered_device_path) {
287   PortableDeviceMapService::GetInstance()->RemovePortableDevice(
288       registered_device_path);
289 }
290 
291 }  // namespace
292 
293 // Used by CreateMTPDeviceAsyncDelegate() to create the MTP device
294 // delegate on the IO thread.
OnGetStorageInfoCreateDelegate(const base::string16 & device_location,const CreateMTPDeviceAsyncDelegateCallback & callback,base::string16 * pnp_device_id,base::string16 * storage_object_id,bool succeeded)295 void OnGetStorageInfoCreateDelegate(
296     const base::string16& device_location,
297     const CreateMTPDeviceAsyncDelegateCallback& callback,
298     base::string16* pnp_device_id,
299     base::string16* storage_object_id,
300     bool succeeded) {
301   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
302   DCHECK(pnp_device_id);
303   DCHECK(storage_object_id);
304   if (!succeeded)
305     return;
306   callback.Run(new MTPDeviceDelegateImplWin(device_location,
307                                             *pnp_device_id,
308                                             *storage_object_id));
309 }
310 
CreateMTPDeviceAsyncDelegate(const base::string16 & device_location,const bool read_only,const CreateMTPDeviceAsyncDelegateCallback & callback)311 void CreateMTPDeviceAsyncDelegate(
312     const base::string16& device_location,
313     const bool read_only,
314     const CreateMTPDeviceAsyncDelegateCallback& callback) {
315   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
316 
317   // Write operation is not supported on Windows.
318   DCHECK(read_only);
319 
320   DCHECK(!device_location.empty());
321   base::string16* pnp_device_id = new base::string16;
322   base::string16* storage_object_id = new base::string16;
323   content::GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult(
324       FROM_HERE,
325       base::BindOnce(&GetStorageInfoOnUIThread, device_location,
326                      base::Unretained(pnp_device_id),
327                      base::Unretained(storage_object_id)),
328       base::BindOnce(&OnGetStorageInfoCreateDelegate, device_location, callback,
329                      base::Owned(pnp_device_id),
330                      base::Owned(storage_object_id)));
331 }
332 
333 // MTPDeviceDelegateImplWin ---------------------------------------------------
334 
StorageDeviceInfo(const base::string16 & pnp_device_id,const base::string16 & registered_device_path,const base::string16 & storage_object_id)335 MTPDeviceDelegateImplWin::StorageDeviceInfo::StorageDeviceInfo(
336     const base::string16& pnp_device_id,
337     const base::string16& registered_device_path,
338     const base::string16& storage_object_id)
339     : pnp_device_id(pnp_device_id),
340       registered_device_path(registered_device_path),
341       storage_object_id(storage_object_id) {
342   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
343 }
344 
PendingTaskInfo(const base::Location & location,base::OnceCallback<base::File::Error (void)> task,base::OnceCallback<void (base::File::Error)> reply)345 MTPDeviceDelegateImplWin::PendingTaskInfo::PendingTaskInfo(
346     const base::Location& location,
347     base::OnceCallback<base::File::Error(void)> task,
348     base::OnceCallback<void(base::File::Error)> reply)
349     : location(location), task(std::move(task)), reply(std::move(reply)) {}
350 
351 MTPDeviceDelegateImplWin::PendingTaskInfo::PendingTaskInfo(
352     PendingTaskInfo&& other) = default;
353 
354 MTPDeviceDelegateImplWin::PendingTaskInfo::~PendingTaskInfo() = default;
355 
MTPDeviceDelegateImplWin(const base::string16 & registered_device_path,const base::string16 & pnp_device_id,const base::string16 & storage_object_id)356 MTPDeviceDelegateImplWin::MTPDeviceDelegateImplWin(
357     const base::string16& registered_device_path,
358     const base::string16& pnp_device_id,
359     const base::string16& storage_object_id)
360     : init_state_(UNINITIALIZED),
361       media_task_runner_(MediaFileSystemBackend::MediaTaskRunner()),
362       storage_device_info_(pnp_device_id,
363                            registered_device_path,
364                            storage_object_id),
365       task_in_progress_(false) {
366   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
367   DCHECK(!registered_device_path.empty());
368   DCHECK(!pnp_device_id.empty());
369   DCHECK(!storage_object_id.empty());
370   DCHECK(media_task_runner_.get());
371 }
372 
~MTPDeviceDelegateImplWin()373 MTPDeviceDelegateImplWin::~MTPDeviceDelegateImplWin() {
374   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
375 }
376 
GetFileInfo(const base::FilePath & file_path,const GetFileInfoSuccessCallback & success_callback,const ErrorCallback & error_callback)377 void MTPDeviceDelegateImplWin::GetFileInfo(
378     const base::FilePath& file_path,
379     const GetFileInfoSuccessCallback& success_callback,
380     const ErrorCallback& error_callback) {
381   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
382   DCHECK(!file_path.empty());
383   base::File::Info* file_info = new base::File::Info;
384   EnsureInitAndRunTask(PendingTaskInfo(
385       FROM_HERE,
386       base::BindOnce(&GetFileInfoOnBlockingPoolThread, storage_device_info_,
387                      file_path, base::Unretained(file_info)),
388       base::BindOnce(&MTPDeviceDelegateImplWin::OnGetFileInfo,
389                      weak_ptr_factory_.GetWeakPtr(), success_callback,
390                      error_callback, base::Owned(file_info))));
391 }
392 
CreateDirectory(const base::FilePath & directory_path,const bool exclusive,const bool recursive,const CreateDirectorySuccessCallback & success_callback,const ErrorCallback & error_callback)393 void MTPDeviceDelegateImplWin::CreateDirectory(
394     const base::FilePath& directory_path,
395     const bool exclusive,
396     const bool recursive,
397     const CreateDirectorySuccessCallback& success_callback,
398     const ErrorCallback& error_callback) {
399   NOTREACHED();
400 }
401 
ReadDirectory(const base::FilePath & root,const ReadDirectorySuccessCallback & success_callback,const ErrorCallback & error_callback)402 void MTPDeviceDelegateImplWin::ReadDirectory(
403     const base::FilePath& root,
404     const ReadDirectorySuccessCallback& success_callback,
405     const ErrorCallback& error_callback) {
406   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
407   DCHECK(!root.empty());
408   storage::AsyncFileUtil::EntryList* entries =
409       new storage::AsyncFileUtil::EntryList;
410   EnsureInitAndRunTask(PendingTaskInfo(
411       FROM_HERE,
412       base::BindOnce(&ReadDirectoryOnBlockingPoolThread, storage_device_info_,
413                      root, base::Unretained(entries)),
414       base::BindOnce(&MTPDeviceDelegateImplWin::OnDidReadDirectory,
415                      weak_ptr_factory_.GetWeakPtr(), success_callback,
416                      error_callback, base::Owned(entries))));
417 }
418 
CreateSnapshotFile(const base::FilePath & device_file_path,const base::FilePath & snapshot_file_path,const CreateSnapshotFileSuccessCallback & success_callback,const ErrorCallback & error_callback)419 void MTPDeviceDelegateImplWin::CreateSnapshotFile(
420     const base::FilePath& device_file_path,
421     const base::FilePath& snapshot_file_path,
422     const CreateSnapshotFileSuccessCallback& success_callback,
423     const ErrorCallback& error_callback) {
424   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
425   DCHECK(!device_file_path.empty());
426   DCHECK(!snapshot_file_path.empty());
427   std::unique_ptr<SnapshotFileDetails> file_details(new SnapshotFileDetails(
428       SnapshotRequestInfo(device_file_path, snapshot_file_path,
429                           success_callback, error_callback)));
430   // Passing a raw SnapshotFileDetails* to the blocking pool is safe, because
431   // it is owned by |file_details| in the reply callback.
432   EnsureInitAndRunTask(PendingTaskInfo(
433       FROM_HERE,
434       base::BindOnce(&GetFileStreamOnBlockingPoolThread, storage_device_info_,
435                      file_details.get()),
436       base::BindOnce(&MTPDeviceDelegateImplWin::OnGetFileStream,
437                      weak_ptr_factory_.GetWeakPtr(), std::move(file_details))));
438 }
439 
IsStreaming()440 bool MTPDeviceDelegateImplWin::IsStreaming() {
441   return false;
442 }
443 
ReadBytes(const base::FilePath & device_file_path,const scoped_refptr<net::IOBuffer> & buf,int64_t offset,int buf_len,const ReadBytesSuccessCallback & success_callback,const ErrorCallback & error_callback)444 void MTPDeviceDelegateImplWin::ReadBytes(
445     const base::FilePath& device_file_path,
446     const scoped_refptr<net::IOBuffer>& buf,
447     int64_t offset,
448     int buf_len,
449     const ReadBytesSuccessCallback& success_callback,
450     const ErrorCallback& error_callback) {
451   NOTREACHED();
452 }
453 
IsReadOnly() const454 bool MTPDeviceDelegateImplWin::IsReadOnly() const {
455   return true;
456 }
457 
CopyFileLocal(const base::FilePath & source_file_path,const base::FilePath & device_file_path,const CreateTemporaryFileCallback & create_temporary_file_callback,const CopyFileProgressCallback & progress_callback,const CopyFileLocalSuccessCallback & success_callback,const ErrorCallback & error_callback)458 void MTPDeviceDelegateImplWin::CopyFileLocal(
459     const base::FilePath& source_file_path,
460     const base::FilePath& device_file_path,
461     const CreateTemporaryFileCallback& create_temporary_file_callback,
462     const CopyFileProgressCallback& progress_callback,
463     const CopyFileLocalSuccessCallback& success_callback,
464     const ErrorCallback& error_callback) {
465   NOTREACHED();
466 }
467 
MoveFileLocal(const base::FilePath & source_file_path,const base::FilePath & device_file_path,const CreateTemporaryFileCallback & create_temporary_file_callback,const MoveFileLocalSuccessCallback & success_callback,const ErrorCallback & error_callback)468 void MTPDeviceDelegateImplWin::MoveFileLocal(
469     const base::FilePath& source_file_path,
470     const base::FilePath& device_file_path,
471     const CreateTemporaryFileCallback& create_temporary_file_callback,
472     const MoveFileLocalSuccessCallback& success_callback,
473     const ErrorCallback& error_callback) {
474   NOTREACHED();
475 }
476 
CopyFileFromLocal(const base::FilePath & source_file_path,const base::FilePath & device_file_path,const CopyFileFromLocalSuccessCallback & success_callback,const ErrorCallback & error_callback)477 void MTPDeviceDelegateImplWin::CopyFileFromLocal(
478     const base::FilePath& source_file_path,
479     const base::FilePath& device_file_path,
480     const CopyFileFromLocalSuccessCallback& success_callback,
481     const ErrorCallback& error_callback) {
482   NOTREACHED();
483 }
484 
DeleteFile(const base::FilePath & file_path,const DeleteFileSuccessCallback & success_callback,const ErrorCallback & error_callback)485 void MTPDeviceDelegateImplWin::DeleteFile(
486     const base::FilePath& file_path,
487     const DeleteFileSuccessCallback& success_callback,
488     const ErrorCallback& error_callback) {
489   NOTREACHED();
490 }
491 
DeleteDirectory(const base::FilePath & file_path,const DeleteDirectorySuccessCallback & success_callback,const ErrorCallback & error_callback)492 void MTPDeviceDelegateImplWin::DeleteDirectory(
493     const base::FilePath& file_path,
494     const DeleteDirectorySuccessCallback& success_callback,
495     const ErrorCallback& error_callback) {
496   NOTREACHED();
497 }
498 
AddWatcher(const GURL & origin,const base::FilePath & file_path,const bool recursive,storage::WatcherManager::StatusCallback callback,storage::WatcherManager::NotificationCallback notification_callback)499 void MTPDeviceDelegateImplWin::AddWatcher(
500     const GURL& origin,
501     const base::FilePath& file_path,
502     const bool recursive,
503     storage::WatcherManager::StatusCallback callback,
504     storage::WatcherManager::NotificationCallback notification_callback) {
505   NOTIMPLEMENTED();
506   std::move(callback).Run(base::File::FILE_ERROR_INVALID_OPERATION);
507 }
508 
RemoveWatcher(const GURL & origin,const base::FilePath & file_path,const bool recursive,storage::WatcherManager::StatusCallback callback)509 void MTPDeviceDelegateImplWin::RemoveWatcher(
510     const GURL& origin,
511     const base::FilePath& file_path,
512     const bool recursive,
513     storage::WatcherManager::StatusCallback callback) {
514   NOTIMPLEMENTED();
515   std::move(callback).Run(base::File::FILE_ERROR_INVALID_OPERATION);
516 }
517 
CancelPendingTasksAndDeleteDelegate()518 void MTPDeviceDelegateImplWin::CancelPendingTasksAndDeleteDelegate() {
519   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
520   PortableDeviceMapService::GetInstance()->MarkPortableDeviceForDeletion(
521       storage_device_info_.registered_device_path);
522   media_task_runner_->PostTask(
523       FROM_HERE, base::BindOnce(&DeletePortableDeviceOnBlockingPoolThread,
524                                 storage_device_info_.registered_device_path));
525   while (!pending_tasks_.empty())
526     pending_tasks_.pop();
527   delete this;
528 }
529 
EnsureInitAndRunTask(PendingTaskInfo task_info)530 void MTPDeviceDelegateImplWin::EnsureInitAndRunTask(PendingTaskInfo task_info) {
531   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
532   if ((init_state_ == INITIALIZED) && !task_in_progress_) {
533     DCHECK(pending_tasks_.empty());
534     DCHECK(!current_snapshot_details_.get());
535     base::PostTaskAndReplyWithResult(
536         media_task_runner_.get(), task_info.location, std::move(task_info.task),
537         std::move(task_info.reply));
538     task_in_progress_ = true;
539     return;
540   }
541 
542   pending_tasks_.push(std::move(task_info));
543   if (init_state_ == UNINITIALIZED) {
544     init_state_ = PENDING_INIT;
545     base::PostTaskAndReplyWithResult(
546         media_task_runner_.get(), FROM_HERE,
547         base::BindOnce(&OpenDeviceOnBlockingPoolThread,
548                        storage_device_info_.pnp_device_id,
549                        storage_device_info_.registered_device_path),
550         base::BindOnce(&MTPDeviceDelegateImplWin::OnInitCompleted,
551                        weak_ptr_factory_.GetWeakPtr()));
552     task_in_progress_ = true;
553   }
554 }
555 
WriteDataChunkIntoSnapshotFile()556 void MTPDeviceDelegateImplWin::WriteDataChunkIntoSnapshotFile() {
557   DCHECK(current_snapshot_details_.get());
558   base::PostTaskAndReplyWithResult(
559       media_task_runner_.get(), FROM_HERE,
560       base::BindOnce(&WriteDataChunkIntoSnapshotFileOnBlockingPoolThread,
561                      *current_snapshot_details_),
562       base::BindOnce(
563           &MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile,
564           weak_ptr_factory_.GetWeakPtr(),
565           current_snapshot_details_->request_info().snapshot_file_path));
566 }
567 
ProcessNextPendingRequest()568 void MTPDeviceDelegateImplWin::ProcessNextPendingRequest() {
569   DCHECK(!task_in_progress_);
570   if (pending_tasks_.empty())
571     return;
572   PendingTaskInfo& task_info = pending_tasks_.front();
573   task_in_progress_ = true;
574   base::PostTaskAndReplyWithResult(media_task_runner_.get(), task_info.location,
575                                    std::move(task_info.task),
576                                    std::move(task_info.reply));
577   pending_tasks_.pop();
578 }
579 
OnInitCompleted(bool succeeded)580 void MTPDeviceDelegateImplWin::OnInitCompleted(bool succeeded) {
581   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
582   init_state_ = succeeded ? INITIALIZED : UNINITIALIZED;
583   task_in_progress_ = false;
584   ProcessNextPendingRequest();
585 }
586 
OnGetFileInfo(const GetFileInfoSuccessCallback & success_callback,const ErrorCallback & error_callback,base::File::Info * file_info,base::File::Error error)587 void MTPDeviceDelegateImplWin::OnGetFileInfo(
588     const GetFileInfoSuccessCallback& success_callback,
589     const ErrorCallback& error_callback,
590     base::File::Info* file_info,
591     base::File::Error error) {
592   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
593   DCHECK(file_info);
594   if (error == base::File::FILE_OK)
595     success_callback.Run(*file_info);
596   else
597     error_callback.Run(error);
598   task_in_progress_ = false;
599   ProcessNextPendingRequest();
600 }
601 
OnDidReadDirectory(const ReadDirectorySuccessCallback & success_callback,const ErrorCallback & error_callback,storage::AsyncFileUtil::EntryList * file_list,base::File::Error error)602 void MTPDeviceDelegateImplWin::OnDidReadDirectory(
603     const ReadDirectorySuccessCallback& success_callback,
604     const ErrorCallback& error_callback,
605     storage::AsyncFileUtil::EntryList* file_list,
606     base::File::Error error) {
607   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
608   DCHECK(file_list);
609   if (error == base::File::FILE_OK)
610     success_callback.Run(*file_list, false /*no more entries*/);
611   else
612     error_callback.Run(error);
613   task_in_progress_ = false;
614   ProcessNextPendingRequest();
615 }
616 
OnGetFileStream(std::unique_ptr<SnapshotFileDetails> file_details,base::File::Error error)617 void MTPDeviceDelegateImplWin::OnGetFileStream(
618     std::unique_ptr<SnapshotFileDetails> file_details,
619     base::File::Error error) {
620   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
621   DCHECK(file_details);
622   DCHECK(!file_details->request_info().device_file_path.empty());
623   DCHECK(!file_details->request_info().snapshot_file_path.empty());
624   DCHECK(!current_snapshot_details_.get());
625   if (error != base::File::FILE_OK) {
626     file_details->request_info().error_callback.Run(error);
627     task_in_progress_ = false;
628     ProcessNextPendingRequest();
629     return;
630   }
631   DCHECK(file_details->file_info().size == 0 ||
632          file_details->device_file_stream());
633   current_snapshot_details_ = std::move(file_details);
634   WriteDataChunkIntoSnapshotFile();
635 }
636 
OnWroteDataChunkIntoSnapshotFile(const base::FilePath & snapshot_file_path,DWORD bytes_written)637 void MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile(
638     const base::FilePath& snapshot_file_path,
639     DWORD bytes_written) {
640   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
641   DCHECK(!snapshot_file_path.empty());
642   if (!current_snapshot_details_.get())
643     return;
644   DCHECK_EQ(
645       current_snapshot_details_->request_info().snapshot_file_path.value(),
646       snapshot_file_path.value());
647 
648   bool succeeded = false;
649   bool should_continue = false;
650   if (current_snapshot_details_->file_info().size > 0) {
651     if (current_snapshot_details_->AddBytesWritten(bytes_written)) {
652       if (current_snapshot_details_->IsSnapshotFileWriteComplete()) {
653         succeeded = true;
654       } else {
655         should_continue = true;
656       }
657     }
658   } else {
659     // Handle empty files.
660     DCHECK_EQ(0U, bytes_written);
661     succeeded = true;
662   }
663 
664   if (should_continue) {
665     WriteDataChunkIntoSnapshotFile();
666     return;
667   }
668   if (succeeded) {
669     current_snapshot_details_->request_info().success_callback.Run(
670         current_snapshot_details_->file_info(),
671         current_snapshot_details_->request_info().snapshot_file_path);
672   } else {
673     current_snapshot_details_->request_info().error_callback.Run(
674         base::File::FILE_ERROR_FAILED);
675   }
676   task_in_progress_ = false;
677   current_snapshot_details_.reset();
678   ProcessNextPendingRequest();
679 }
680