Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(140)

Unified Diff: chrome/browser/media_gallery/mtp_device_delegate_impl_win.cc

Issue 11297002: [Media Gallery] Added code to support mtp device media file system on Windows. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: diff against codereview.chromium.org/11088012 Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/media_gallery/mtp_device_delegate_impl_win.cc
diff --git a/chrome/browser/media_gallery/mtp_device_delegate_impl_win.cc b/chrome/browser/media_gallery/mtp_device_delegate_impl_win.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9af7e9481252ec60b03a85af43dcf46fc70468a0
--- /dev/null
+++ b/chrome/browser/media_gallery/mtp_device_delegate_impl_win.cc
@@ -0,0 +1,945 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/media_gallery/mtp_device_delegate_impl_win.h"
+
+#include <PortableDevice.h>
+
+#include <vector>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/sequenced_task_runner.h"
+#include "base/string_split.h"
+#include "base/string_util.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/utf_string_conversions.h"
+#include "base/win/scoped_co_mem.h"
+#include "chrome/browser/system_monitor/removable_device_constants.h"
+#include "chrome/browser/system_monitor/removable_device_notifications_window_win.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
+
+namespace chrome {
+
+namespace {
+
+// Name of the client application that communicates with the mtp device.
+const char16 kClientName[] = L"Chromium";
+
+// File path separator constant.
+const char16 kRootPath[] = L"\\\\";
+
+// Reads data from |stream| into a |data| string.
+HRESULT ReadStream(IStream* stream, size_t size, std::string* data) {
+ DCHECK(stream);
+ DCHECK_GT(size, 0u);
+ DCHECK(data);
+ DWORD read = 0;
+ HRESULT hr = S_OK;
+ do {
+ std::string buffer;
+ hr = stream->Read(WriteInto(&buffer, size + 1), size, &read);
+ DCHECK(hr == S_OK || hr == S_FALSE || hr == E_PENDING);
+ if (read) {
+ buffer.erase(read);
+ DCHECK_EQ(read, buffer.length());
+ *data += buffer;
+ }
+ } while ((read > 0) && SUCCEEDED(hr));
+ return hr;
+}
+
+// Represents Mtp device object entry.
+class MtpObjectEntry {
+ public:
+ MtpObjectEntry();
+ MtpObjectEntry(IPortableDevice*device, const string16& object_id);
+ virtual ~MtpObjectEntry();
+
+ // On success, returns true and populates object details.
+ bool PopulateObjectDetails();
+
+ // Accessor functions to get object details.
+ string16 object_id() { return object_id_; }
+ string16 file_name() { return name_; }
+ int64 size() { return size_; }
+ base::Time last_modified_time() { return last_modified_time_; }
+
+ // Returns true, if the object is of folder/directory/album type.
+ bool is_directory();
+
+ private:
+ // On success, returns true and populates |properties_to_read_| with the
+ // property keys of the object.
+ bool PopulatePropertyKeyCollection();
+
+ // Helper functions to set the property key values.
+ void SetObjectName();
+ void SetContentType();
+ void SetLastModifiedTime();
+ void SetObjectSize();
+
+ // Returns the object file name extension.
+ string16 GetObjectExtension();
+
+ // Stores a pointer to IPortableDevice interface that provides access to the
+ // portable device.
+ base::win::ScopedComPtr<IPortableDevice> device_;
+
+ // Stores a collection of property keys.
+ base::win::ScopedComPtr<IPortableDeviceKeyCollection> properties_to_read_;
+
+ // Stores the object property key values.
+ base::win::ScopedComPtr<IPortableDeviceValues> properties_values_;
+
+ // Stores the identifier of the object.
+ string16 object_id_;
+
+ // Stores the content type (Folder, Directory, Album, File, etc.,) of the
+ // object.
+ GUID content_type_;
+
+ // Stores the friendly name of the object.
+ string16 name_;
+
+ // Stores the size of the object.
+ int64 size_;
+
+ // Stores the last modified time of the object.
+ base::Time last_modified_time_;
+};
+
+MtpObjectEntry::MtpObjectEntry()
+ : device_(NULL),
+ content_type_(GUID_NULL),
+ size_(0),
+ last_modified_time_(base::Time()) {
+}
+
+MtpObjectEntry::MtpObjectEntry(IPortableDevice* device,
+ const string16& object_id)
+ : device_(device),
+ object_id_(object_id),
+ content_type_(GUID_NULL),
+ size_(0),
+ last_modified_time_(base::Time()) {
+}
+
+MtpObjectEntry::~MtpObjectEntry() {
+}
+
+bool MtpObjectEntry::PopulateObjectDetails() {
+ base::win::ScopedComPtr<IPortableDeviceContent> content;
+ HRESULT hr = device_->Content(content.Receive());
+ if (FAILED(hr))
+ return false;
+
+ base::win::ScopedComPtr<IPortableDeviceProperties> properties;
+ hr = content->Properties(properties.Receive());
+ if (FAILED(hr))
+ return false;
+
+ if (!PopulatePropertyKeyCollection())
+ return false;
+
+ hr = properties->GetValues(object_id_.c_str(),
+ properties_to_read_.get(),
+ properties_values_.Receive());
+ if (FAILED(hr))
+ return false;
+
+ SetContentType();
+ SetObjectName();
+ SetObjectSize();
+ SetLastModifiedTime();
+ return true;
+}
+
+bool MtpObjectEntry::is_directory() {
+ return (content_type_ == WPD_CONTENT_TYPE_AUDIO_ALBUM) ||
+ (content_type_ == WPD_CONTENT_TYPE_FOLDER) ||
+ (content_type_ == WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT) ||
+ (content_type_ == WPD_CONTENT_TYPE_IMAGE_ALBUM) ||
+ (content_type_ == WPD_CONTENT_TYPE_MIXED_CONTENT_ALBUM) ||
+ (content_type_ == WPD_CONTENT_TYPE_VIDEO_ALBUM);
+}
+
+bool MtpObjectEntry::PopulatePropertyKeyCollection() {
+ HRESULT hr = properties_to_read_.CreateInstance(
+ __uuidof(PortableDeviceKeyCollection), NULL, CLSCTX_INPROC_SERVER);
+ if (FAILED(hr))
+ return false;
+ return SUCCEEDED(properties_to_read_->Add(WPD_OBJECT_CONTENT_TYPE)) &&
+ SUCCEEDED(properties_to_read_->Add(WPD_OBJECT_FORMAT)) &&
+ SUCCEEDED(properties_to_read_->Add(WPD_OBJECT_NAME)) &&
+ SUCCEEDED(properties_to_read_->Add(WPD_OBJECT_DATE_MODIFIED)) &&
+ SUCCEEDED(properties_to_read_->Add(WPD_OBJECT_SIZE));
+}
+
+void MtpObjectEntry::SetObjectName() {
+ base::win::ScopedCoMem<char16> buffer;
+ HRESULT hr = properties_values_->GetStringValue(WPD_OBJECT_NAME, &buffer);
+ DCHECK(SUCCEEDED(hr));
+ name_ = string16(buffer);
+
+ if (name_.empty() || is_directory())
+ return;
+
+ // Check to see if an extension exists in the given object name.
+ if (name_.find_first_of(L".") != string16::npos)
+ return;
+
+ string16 extension(GetObjectExtension());
+ name_ += extension;
+}
+
+void MtpObjectEntry::SetContentType() {
+ HRESULT hr = properties_values_->GetGuidValue(WPD_OBJECT_CONTENT_TYPE,
+ &content_type_);
+ DCHECK(SUCCEEDED(hr));
+}
+
+void MtpObjectEntry::SetLastModifiedTime() {
+ PROPVARIANT last_modified_date = {0};
+ PropVariantInit(&last_modified_date);
+ HRESULT hr = properties_values_->GetValue(WPD_OBJECT_DATE_MODIFIED,
+ &last_modified_date);
+ if (SUCCEEDED(hr) && (last_modified_date.vt == VT_DATE)) {
+ SYSTEMTIME system_time;
+ FILETIME file_time;
+ VariantTimeToSystemTime(last_modified_date.date, &system_time);
+ SystemTimeToFileTime(&system_time, &file_time);
+ last_modified_time_ = base::Time::FromFileTime(file_time);
+ }
+ PropVariantClear(&last_modified_date);
+}
+
+void MtpObjectEntry::SetObjectSize() {
+ properties_values_->GetUnsignedLargeIntegerValue(
+ WPD_OBJECT_SIZE, reinterpret_cast<ULONGLONG*>(&size_));
+}
+
+string16 MtpObjectEntry::GetObjectExtension() {
+ GUID format;
+ HRESULT hr = properties_values_->GetGuidValue(WPD_OBJECT_FORMAT, &format);
+ DCHECK(SUCCEEDED(hr));
+ if (IsEqualGUID(WPD_OBJECT_FORMAT_BMP, format))
+ return kbmpFormat;
+ if (IsEqualGUID(WPD_OBJECT_FORMAT_EXIF, format))
+ return kexifFormat;
+ if (IsEqualGUID(WPD_OBJECT_FORMAT_GIF, format))
+ return kgifFormat;
+ if (IsEqualGUID(WPD_OBJECT_FORMAT_JFIF, format))
+ return kjfifFormat;
+ if (IsEqualGUID(WPD_OBJECT_FORMAT_JP2, format))
+ return kjp2Format;
+ if (IsEqualGUID(WPD_OBJECT_FORMAT_JPEGXR, format))
+ return kjpegxrFormat;
+ if (IsEqualGUID(WPD_OBJECT_FORMAT_JPX, format))
+ return kjpxFormat;
+ if (IsEqualGUID(WPD_OBJECT_FORMAT_PICT, format))
+ return kpictFormat;
+ if (IsEqualGUID(WPD_OBJECT_FORMAT_PNG, format))
+ return kpngFormat;
+ return string16();
+}
+
+// Returns the identifier of the object specified by |object_name|. |parent_id|
+// specifies the object's parent identifier.
+string16 GetObjectIdFromName(IPortableDevice* device,
+ const string16& parent_id,
+ const string16& object_name) {
+ base::win::ScopedComPtr<IPortableDeviceContent> content;
+ HRESULT hr = device->Content(content.Receive());
+ if (FAILED(hr))
+ return string16();
+
+ base::win::ScopedComPtr<IEnumPortableDeviceObjectIDs> enum_object_ids;
+ hr = content->EnumObjects(0, parent_id.c_str(), NULL,
+ enum_object_ids.Receive());
+ if (FAILED(hr))
+ return string16();
+
+ DWORD num_objects_to_request = 10;
+ while (hr == S_OK) {
+ DWORD num_objects_fetched = 0;
+ scoped_array<LPWSTR> object_id_list(new LPWSTR[num_objects_to_request]);
+ hr = enum_object_ids->Next(num_objects_to_request, object_id_list.get(),
+ &num_objects_fetched);
+ for (DWORD index = 0; index < num_objects_fetched; index++) {
+ MtpObjectEntry entry(device, object_id_list[index]);
+ if (!entry.PopulateObjectDetails())
+ return string16();
+ if (entry.file_name() == object_name)
+ return entry.object_id();
+ }
+ }
+ return string16();
+}
+
+// Returns the object id of the file specified by the |file_path|.
+// E.g.: If the |file_path| is "\\mtp:StorageSerial:SID-{10001,,1922}:125\DCIM"
+// and |registered_dev_path| is "\\mtp:StorageSerial:SID-{10001,,1922}:125",
+// this function returns the object id of "DCIM" folder object.
+string16 GetObjectIdFromFilePath(IPortableDevice* device,
+ const string16& registered_dev_path,
+ const string16& file_path,
+ const string16& root_storage_object_id) {
+ DCHECK(!registered_dev_path.empty());
+ DCHECK(!file_path.empty());
+ if (registered_dev_path == file_path)
+ return root_storage_object_id;
+
+ string16 actual_file_path = file_path;
+ ReplaceFirstSubstringAfterOffset(&actual_file_path, 0,
+ registered_dev_path, L"");
+ DCHECK(!actual_file_path.empty());
+ std::vector<string16> path_components;
+ base::SplitString(actual_file_path, L'\\', &path_components);
+ DCHECK(path_components.size() >= 1);
+ string16 parent_id = root_storage_object_id;
+ string16 file_object_id;
+ for (size_t index = 1; index < path_components.size(); ++index) {
+ file_object_id = GetObjectIdFromName(device, parent_id,
+ path_components[index]);
+ parent_id = file_object_id;
+ }
+ return file_object_id;
+}
+
+// Gets the storage details from a storage path. On success, returns true and
+// fills in |pnp_device_id| and |storage_object_id|. |pnp_device_id| specifies
+// the plug and play device ID string. |storage_object_id| specifies a
+// temporary identifier that uniquely identifies the storage object in the
+// device.
+bool GetStorageInfoFromStoragePath(const string16& storage_path,
+ string16* pnp_device_id,
+ string16* storage_object_id) {
+ string16 storage_unique_id;
+ RemoveChars(storage_path, kRootPath, &storage_unique_id);
+ DCHECK(!storage_unique_id.empty());
+ RemovableDeviceNotificationsWindowWin* notifications =
+ RemovableDeviceNotificationsWindowWin::GetInstance();
+ return notifications->GetMtpStorageInfoFromUniqueId(
+ UTF16ToUTF8(storage_unique_id), pnp_device_id, storage_object_id);
+}
+
+// Worker class to open a connection between the application and the device.
+class OpenStorageWorker {
+ public:
+ // Constructed on |media_task_runner_| thread.
+ OpenStorageWorker(const string16& pnp_device_id,
+ base::WaitableEvent* shutdown_event);
+ virtual ~OpenStorageWorker();
+
+ // Opens the device for communication.
+ void Run();
+
+ // Returns a reference to portable device interface if the
+ // IPortableDevice::Open() request was successfully completed.
+ IPortableDevice* device() const;
+
+ private:
+ // Sets up client details to open a connection between the application and
+ // the device.
+ bool SetClientInformation();
+
+ // Stores the plug and play device ID string to open the device.
+ const string16 pnp_device_id_;
+
+ // Stores a pointer to IPortableDevice interface that provides access to a
+ // portable device.
+ base::win::ScopedComPtr<IPortableDevice> device_;
+
+ // Stores a pointer to an IPortableDeviceValues interface that holds
+ // information that identifies the application to the device.
+ base::win::ScopedComPtr<IPortableDeviceValues> client_info_;
+
+ // Stores a reference to waitable event associated with the shut down message.
+ base::WaitableEvent* on_shutdown_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(OpenStorageWorker);
+};
+
+// Worker class to get media device file information given an |object_id|.
+class GetFileInfoWorker {
+ public:
+ GetFileInfoWorker(IPortableDevice* device,
+ const string16& object_id,
+ base::WaitableEvent* shutdown_event);
+
+ virtual ~GetFileInfoWorker();
+
+ // Gets the media device object details.
+ void Run();
+
+ // Returns the request result and fills in |file_info| with requested file
+ // entry details.
+ base::PlatformFileError get_file_info(base::PlatformFileInfo* file_info)
+ const;
+
+ private:
+ // Stores the result of get file info request.
+ base::PlatformFileError error_;
+
+ // Stores the media file entry information.
+ base::PlatformFileInfo file_entry_info_;
+
+ // Stores the requested object identifier.
+ const string16 object_id_;
+
+ // Stores a pointer to IPortableDevice interface that provides access to the
+ // portable device.
+ base::win::ScopedComPtr<IPortableDevice> device_;
+
+ // Stores a reference to waitable event associated with the shut down message.
+ base::WaitableEvent* on_shutdown_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(GetFileInfoWorker);
+};
+
+// Worker class to read media device file contents given a |file_object_id|.
+class ReadFileWorker {
+ public:
+ // Constructed on |media_task_runner_| thread.
+ ReadFileWorker(IPortableDevice* device,
+ const string16& file_object_id,
+ base::WaitableEvent* shutdown_event);
+
+ virtual ~ReadFileWorker();
+
+ // Reads the media device file contents. On success, |data_| has the media
+ // file contents.
+ void Run();
+
+ // Returns the media device file contents.
+ const std::string& data() const { return data_; }
+
+ private:
+ // Stores the media file object identifier.
+ const string16 file_object_id_;
+
+ // Stores a pointer to IPortableDevice interface that provides access to the
+ // portable device.
+ base::win::ScopedComPtr<IPortableDevice> device_;
+
+ // Stores the media device file contents.
+ std::string data_;
+
+ // Stores a reference to waitable event associated with the shut down message.
+ base::WaitableEvent* on_shutdown_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadFileWorker);
+};
+
+// Worker class to read directory contents. Device is already opened for
+// communication.
+class ReadDirectoryWorker {
+ public:
+ ReadDirectoryWorker(IPortableDevice* device,
+ const string16& dir_object_id,
+ base::WaitableEvent* shutdown_event);
+ virtual ~ReadDirectoryWorker();
+
+ // Reads the specified directory contents.
+ void Run();
+
+ // Returns the directory entries for the given directory path.
+ const std::vector<MtpObjectEntry>& get_file_entries() const {
+ return file_entries_;
+ }
+
+ private:
+ // Stores the directory object identifier.
+ const string16 directory_object_id_;
+
+ // Stores a pointer to IPortableDevice interface that provides access to the
+ // portable device.
+ base::win::ScopedComPtr<IPortableDevice> device_;
+
+ // Stores the result of read directory request.
+ std::vector<MtpObjectEntry> file_entries_;
+
+ // Stores a reference to waitable event associated with the shut down message.
+ base::WaitableEvent* on_shutdown_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadDirectoryWorker);
+};
+
+// Simply enumerate each files from a given file entry list.
+// Used to enumerate top-level files of an media file system.
+class MediaFileEnumerator
+ : public fileapi::FileSystemFileUtil::AbstractFileEnumerator {
+ public:
+ explicit MediaFileEnumerator(const std::vector<MtpObjectEntry>& entries);
+ virtual ~MediaFileEnumerator();
+
+ // AbstractFileEnumerator override.
+ virtual FilePath Next() OVERRIDE;
+ virtual int64 Size() OVERRIDE;
+ virtual bool IsDirectory() OVERRIDE;
+ virtual base::Time LastModifiedTime() OVERRIDE;
+
+ private:
+ // List of directory file entries information.
+ const std::vector<MtpObjectEntry> file_entries_;
+
+ // Iterator to access the individual file entries.
+ std::vector<MtpObjectEntry>::const_iterator file_entry_iter_;
+
+ // Stores the current file information.
+ MtpObjectEntry current_file_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaFileEnumerator);
+};
+
+// Recursively enumerate each file object entry from a given media file object
+// entry set.
+class RecursiveMediaFileEnumerator
+ : public fileapi::FileSystemFileUtil::AbstractFileEnumerator {
+ public:
+ RecursiveMediaFileEnumerator(IPortableDevice* device,
+ const std::vector<MtpObjectEntry>& entries,
+ base::WaitableEvent* shutdown_event);
+ virtual ~RecursiveMediaFileEnumerator();
+
+ // AbstractFileEnumerator override.
+ virtual FilePath Next() OVERRIDE;
+ virtual int64 Size() OVERRIDE;
+ virtual bool IsDirectory() OVERRIDE;
+ virtual base::Time LastModifiedTime();
+
+ private:
+ // Stores a pointer to IPortableDevice interface that provides access to a
+ // portable device.
+ base::win::ScopedComPtr<IPortableDevice> device_;
+
+ // List of top-level directory file object entries.
+ const std::vector<MtpObjectEntry> file_entries_;
+
+ // Iterator to access the individual file object entries.
+ std::vector<MtpObjectEntry>::const_iterator file_entry_iter_;
+
+ // Enumerator to access current directory Id/path entries.
+ scoped_ptr<fileapi::FileSystemFileUtil::AbstractFileEnumerator>
+ current_enumerator_;
+
+ // Stores a reference to waitable event associated with the shut down message.
+ base::WaitableEvent* on_shutdown_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(RecursiveMediaFileEnumerator);
+};
+
+// OpenStorageWorker implementation.
+OpenStorageWorker::OpenStorageWorker(const string16& pnp_device_id,
+ base::WaitableEvent* shutdown_event)
+ : pnp_device_id_(pnp_device_id),
+ on_shutdown_event_(shutdown_event) {
+}
+
+OpenStorageWorker::~OpenStorageWorker() {
+}
+
+void OpenStorageWorker::Run() {
+ if (on_shutdown_event_->IsSignaled()) {
+ // Process is in shutdown mode.
+ return;
+ }
+
+ if (!SetClientInformation())
+ return;
+
+ HRESULT hr = device_.CreateInstance(__uuidof(PortableDevice), NULL,
+ CLSCTX_INPROC_SERVER);
+ if (FAILED(hr))
+ return;
+
+ hr = device_->Open(const_cast<char16*>(pnp_device_id_.c_str()),
+ client_info_.get());
+ if (FAILED(hr) && (hr == E_ACCESSDENIED))
+ DPLOG(ERROR) << "Access denied to open the device";
+ return;
+}
+
+IPortableDevice* OpenStorageWorker::device() const {
+ base::win::ScopedComPtr<IPortableDevice>* device =
+ const_cast<base::win::ScopedComPtr<IPortableDevice>*>(&device_);
+ return device->Detach();
+}
+
+bool OpenStorageWorker::SetClientInformation() {
+ HRESULT hr = client_info_.CreateInstance(__uuidof(PortableDeviceValues),
+ NULL, CLSCTX_INPROC_SERVER);
+ if (FAILED(hr))
+ return false;
+
+ // Attempt to set the basic client information.
+ client_info_->SetStringValue(WPD_CLIENT_NAME, kClientName);
+ client_info_->SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, 0);
+ client_info_->SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, 0);
+ client_info_->SetUnsignedIntegerValue(WPD_CLIENT_REVISION, 0);
+ client_info_->SetUnsignedIntegerValue(WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE,
+ SECURITY_IMPERSONATION);
+ client_info_->SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS,
+ GENERIC_READ);
+ return true;
+}
+
+// GetFileInfoWorker Implementation.
+GetFileInfoWorker::GetFileInfoWorker(IPortableDevice* device,
+ const string16& object_id,
+ base::WaitableEvent* shutdown_event)
+ : device_(device),
+ object_id_(object_id),
+ error_(base::PLATFORM_FILE_OK),
+ on_shutdown_event_(shutdown_event) {
+}
+
+GetFileInfoWorker::~GetFileInfoWorker() {
+}
+
+void GetFileInfoWorker::Run() {
+ if (on_shutdown_event_->IsSignaled()) {
+ // Process is in shutdown mode.
+ return;
+ }
+
+ MtpObjectEntry entry(device_.get(), object_id_);
+ if (!entry.PopulateObjectDetails()) {
+ error_ = base::PLATFORM_FILE_ERROR_NOT_FOUND;
+ return;
+ }
+
+ file_entry_info_.size = entry.size();
+ file_entry_info_.is_directory = entry.is_directory();
+ file_entry_info_.is_symbolic_link = false;
+ file_entry_info_.last_modified = entry.last_modified_time();
+ file_entry_info_.last_accessed = entry.last_modified_time();
+ file_entry_info_.creation_time = base::Time();
+}
+
+base::PlatformFileError GetFileInfoWorker::get_file_info(
+ base::PlatformFileInfo* file_info) const {
+ if (file_info)
+ *file_info = file_entry_info_;
+ return error_;
+}
+
+// ReadFileWorker class implementation.
+ReadFileWorker::ReadFileWorker(IPortableDevice* device,
+ const string16& file_object_id,
+ base::WaitableEvent* shutdown_event)
+ : device_(device),
+ file_object_id_(file_object_id),
+ on_shutdown_event_(shutdown_event) {
+}
+
+ReadFileWorker::~ReadFileWorker() {
+}
+
+void ReadFileWorker::Run() {
+ if (on_shutdown_event_->IsSignaled()) {
+ // Process is in shutdown mode.
+ return;
+ }
+
+ base::win::ScopedComPtr<IPortableDeviceContent> content;
+ HRESULT hr = device_->Content(content.Receive());
+ if (FAILED(hr))
+ return;
+
+ base::win::ScopedComPtr<IPortableDeviceResources> resources;
+ hr = content->Transfer(resources.Receive());
+ if (FAILED(hr))
+ return;
+
+ base::win::ScopedComPtr<IStream> file_stream;
+ DWORD optimal_transfer_size = 0;
+ hr = resources->GetStream(file_object_id_.c_str(), WPD_RESOURCE_DEFAULT,
+ STGM_READ, &optimal_transfer_size,
+ file_stream.Receive());
+ if (FAILED(hr))
+ return;
+ ReadStream(file_stream.get(), optimal_transfer_size, &data_);
+}
+
+// ReadDirectoryWorker class implementation.
+ReadDirectoryWorker::ReadDirectoryWorker(IPortableDevice* device,
+ const string16& dir_object_id,
+ base::WaitableEvent* shutdown_event)
+ : device_(device),
+ directory_object_id_(dir_object_id),
+ on_shutdown_event_(shutdown_event) {
+ DCHECK(!directory_object_id_.empty());
+}
+
+ReadDirectoryWorker::~ReadDirectoryWorker() {
+}
+
+void ReadDirectoryWorker::Run() {
+ if (on_shutdown_event_->IsSignaled()) {
+ // Process is in shutdown mode.
+ return;
+ }
+
+ base::win::ScopedComPtr<IPortableDeviceContent> content;
+ HRESULT hr = device_->Content(content.Receive());
+ if (FAILED(hr))
+ return;
+
+ base::win::ScopedComPtr<IEnumPortableDeviceObjectIDs> enum_object_ids;
+ hr = content->EnumObjects(0, directory_object_id_.c_str(), NULL,
+ enum_object_ids.Receive());
+ if (FAILED(hr))
+ return;
+
+ // Loop calling Next() while S_OK is being returned.
+ DWORD num_objects_to_request = 10;
+ while (hr == S_OK) {
+ DWORD num_objects_fetched = 0;
+ scoped_array<LPWSTR> object_id_list(new LPWSTR[num_objects_to_request]);
+ hr = enum_object_ids->Next(num_objects_to_request, object_id_list.get(),
+ &num_objects_fetched);
+ for (DWORD index = 0; index < num_objects_fetched; index++) {
+ MtpObjectEntry entry(device_.get(), object_id_list[index]);
+ if (entry.PopulateObjectDetails())
+ file_entries_.push_back(entry);
+ }
+ }
+}
+
+// MediaFileEnumerator class implementation.
+MediaFileEnumerator::MediaFileEnumerator(
+ const std::vector<MtpObjectEntry>& entries)
+ : file_entries_(entries),
+ file_entry_iter_(file_entries_.begin()) {
+}
+
+MediaFileEnumerator::~MediaFileEnumerator() {
+}
+
+FilePath MediaFileEnumerator::Next() {
+ if (file_entry_iter_ == file_entries_.end())
+ return FilePath();
+
+ current_file_info_ = *file_entry_iter_;
+ ++file_entry_iter_;
+ return FilePath(current_file_info_.file_name());
+}
+
+int64 MediaFileEnumerator::Size() {
+ return current_file_info_.size();
+}
+
+bool MediaFileEnumerator::IsDirectory() {
+ return current_file_info_.is_directory();
+}
+
+base::Time MediaFileEnumerator::LastModifiedTime() {
+ return current_file_info_.last_modified_time();
+}
+
+// RecursiveMediaFileEnumerator class implementation.
+RecursiveMediaFileEnumerator::RecursiveMediaFileEnumerator(
+ IPortableDevice* device,
+ const std::vector<MtpObjectEntry>& entries,
+ base::WaitableEvent* shutdown_event)
+ : device_(device),
+ file_entries_(entries),
+ file_entry_iter_(file_entries_.begin()),
+ on_shutdown_event_(shutdown_event) {
+ current_enumerator_.reset(new MediaFileEnumerator(entries));
+}
+
+RecursiveMediaFileEnumerator::~RecursiveMediaFileEnumerator() {
+}
+
+FilePath RecursiveMediaFileEnumerator::Next() {
+ if (on_shutdown_event_->IsSignaled()) {
+ // Process is in shutdown mode.
+ return FilePath();
+ }
+
+ FilePath path = current_enumerator_->Next();
+ if (!path.empty())
+ return path;
+
+ // We reached the end.
+ if (file_entry_iter_ == file_entries_.end())
+ return FilePath();
+
+ // Enumerate subdirectories of the next media file entry.
+ MtpObjectEntry next_file_entry = *file_entry_iter_;
+ ++file_entry_iter_;
+
+ // Create a ReadDirectoryWorker object to enumerate sub directories.
+ scoped_ptr<ReadDirectoryWorker> worker(new ReadDirectoryWorker(
+ device_.get(), next_file_entry.object_id(), on_shutdown_event_));
+ worker->Run();
+ if (!worker->get_file_entries().empty()) {
+ current_enumerator_.reset(
+ new MediaFileEnumerator(worker->get_file_entries()));
+ } else {
+ current_enumerator_.reset(
+ new fileapi::FileSystemFileUtil::EmptyFileEnumerator());
+ }
+ return current_enumerator_->Next();
+}
+
+int64 RecursiveMediaFileEnumerator::Size(){
+ return current_enumerator_->Size();
+}
+
+bool RecursiveMediaFileEnumerator::IsDirectory() {
+ return current_enumerator_->IsDirectory();
+}
+
+base::Time RecursiveMediaFileEnumerator::LastModifiedTime() {
+ return current_enumerator_->LastModifiedTime();
+}
+
+} // namespace
+
+MtpDeviceDelegateImplWin::MtpDeviceDelegateImplWin(const string16& fs_root_path)
+ : registered_dev_path_(fs_root_path),
+ on_shutdown_event_(true, false) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
+ base::SequencedWorkerPool::SequenceToken media_sequence_token =
+ pool->GetNamedSequenceToken("media-task-runner");
+ media_task_runner_ = pool->GetSequencedTaskRunner(media_sequence_token);
+ DCHECK(media_task_runner_);
+
+ if (!GetStorageInfoFromStoragePath(registered_dev_path_, &pnp_device_id_,
+ &storage_object_id_)) {
+ NOTREACHED();
+ }
+ registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
+ content::NotificationService::AllSources());
+}
+
+MtpDeviceDelegateImplWin::~MtpDeviceDelegateImplWin() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+}
+
+base::PlatformFileError MtpDeviceDelegateImplWin::GetFileInfo(
+ const FilePath& file_path,
+ base::PlatformFileInfo* file_info) {
+ if (!LazyInit())
+ return base::PLATFORM_FILE_ERROR_FAILED;
+
+ string16 object_id = GetObjectIdFromFilePath(device_.get(),
+ registered_dev_path_,
+ file_path.value(),
+ storage_object_id_);
+ if (object_id.empty())
+ return base::PLATFORM_FILE_ERROR_FAILED;
+
+ scoped_ptr<GetFileInfoWorker> worker(new GetFileInfoWorker(
+ device_.get(), object_id, &on_shutdown_event_));
+ worker->Run();
+ return worker->get_file_info(file_info);
+}
+
+fileapi::FileSystemFileUtil::AbstractFileEnumerator*
+MtpDeviceDelegateImplWin::CreateFileEnumerator(const FilePath& root,
+ bool recursive) {
+ if (root.value().empty() || !LazyInit())
+ return new fileapi::FileSystemFileUtil::EmptyFileEnumerator();
+
+ string16 object_id = GetObjectIdFromFilePath(device_.get(),
+ registered_dev_path_,
+ root.value(),
+ storage_object_id_);
+ if (object_id.empty())
+ return new fileapi::FileSystemFileUtil::EmptyFileEnumerator();
+
+ scoped_ptr<ReadDirectoryWorker> worker(new ReadDirectoryWorker(
+ device_.get(), object_id, &on_shutdown_event_));
+ worker->Run();
+
+ if (worker->get_file_entries().empty())
+ return new fileapi::FileSystemFileUtil::EmptyFileEnumerator();
+
+ if (recursive) {
+ return new RecursiveMediaFileEnumerator(device_.get(),
+ worker->get_file_entries(),
+ &on_shutdown_event_);
+ }
+ return new MediaFileEnumerator(worker->get_file_entries());
+}
+
+base::PlatformFileError MtpDeviceDelegateImplWin::CreateSnapshotFile(
+ const FilePath& device_file_path,
+ const FilePath& local_path,
+ base::PlatformFileInfo* file_info) {
+ if (!LazyInit())
+ return base::PLATFORM_FILE_ERROR_FAILED;
+
+ string16 file_object_id = GetObjectIdFromFilePath(device_.get(),
+ registered_dev_path_,
+ device_file_path.value(),
+ storage_object_id_);
+ if (file_object_id.empty())
+ return base::PLATFORM_FILE_ERROR_FAILED;
+ scoped_ptr<ReadFileWorker> worker(new ReadFileWorker(device_,
+ file_object_id,
+ &on_shutdown_event_));
+ worker->Run();
+
+ const std::string& file_data = worker->data();
+ int data_size = static_cast<int>(file_data.length());
+ if (file_data.empty() ||
+ file_util::WriteFile(local_path, file_data.c_str(),
+ data_size) != data_size) {
+ return base::PLATFORM_FILE_ERROR_FAILED;
+ }
+
+ base::PlatformFileError error = GetFileInfo(device_file_path, file_info);
+
+ // Modify the last modified time to null. This prevents the time stamp
+ // verfication in LocalFileStreamReader.
+ file_info->last_modified = base::Time();
+ return error;
+}
+
+base::SequencedTaskRunner* MtpDeviceDelegateImplWin::media_task_runner() {
+ return media_task_runner_.get();
+}
+
+void MtpDeviceDelegateImplWin::DeleteOnCorrectThread() const {
+ if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
+ content::BrowserThread::DeleteSoon(content::BrowserThread::UI, FROM_HERE,
+ this);
+ return;
+ }
+ delete this;
+}
+
+void MtpDeviceDelegateImplWin::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type);
+ on_shutdown_event_.Signal();
+}
+
+bool MtpDeviceDelegateImplWin::LazyInit() {
+ DCHECK(media_task_runner_);
+ DCHECK(media_task_runner_->RunsTasksOnCurrentThread());
+
+ if (device_.get())
+ return true; // Already successfully initialized.
+
+ DCHECK(!pnp_device_id_.empty());
+ scoped_ptr<OpenStorageWorker> worker(new OpenStorageWorker(
+ pnp_device_id_, &on_shutdown_event_));
+ worker->Run();
+ device_.Attach(worker->device());
+ return (device_.get() != NULL);
+}
+
+} // namespace chrome

Powered by Google App Engine
This is Rietveld 408576698