| 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
|
|
|