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