| Index: chrome/browser/system_monitor/portable_device_watcher_win.cc
|
| diff --git a/chrome/browser/system_monitor/portable_device_watcher_win.cc b/chrome/browser/system_monitor/portable_device_watcher_win.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..70a9f7f229b2a6ec24e2718c6f3bf46c33d2a06d
|
| --- /dev/null
|
| +++ b/chrome/browser/system_monitor/portable_device_watcher_win.cc
|
| @@ -0,0 +1,611 @@
|
| +// 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.
|
| +//
|
| +// Any tasks that communicates with the portable device may take >100ms to
|
| +// complete. Those tasks should be run on an blocking thread instead of the
|
| +// UI thread.
|
| +
|
| +#include "chrome/browser/system_monitor/portable_device_watcher_win.h"
|
| +
|
| +#include <dbt.h>
|
| +#include <portabledevice.h>
|
| +
|
| +#include "base/file_path.h"
|
| +#include "base/logging.h"
|
| +#include "base/stl_util.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 "base/win/scoped_comptr.h"
|
| +#include "chrome/browser/system_monitor/media_storage_util.h"
|
| +#include "chrome/browser/system_monitor/removable_device_constants.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| +
|
| +namespace chrome {
|
| +
|
| +namespace {
|
| +
|
| +// Name of the client application that communicates with the MTP device.
|
| +const char16 kClientName[] = L"Chromium";
|
| +
|
| +// Name of the sequenced task runner.
|
| +const char kMediaTaskRunnerName[] = "media-task-runner";
|
| +
|
| +// Returns true if |data| represents a class of portable devices.
|
| +bool IsPortableDeviceStructure(LPARAM data) {
|
| + DEV_BROADCAST_HDR* broadcast_hdr =
|
| + reinterpret_cast<DEV_BROADCAST_HDR*>(data);
|
| + if (!broadcast_hdr ||
|
| + (broadcast_hdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)) {
|
| + return false;
|
| + }
|
| +
|
| + GUID guidDevInterface = GUID_NULL;
|
| + if (FAILED(CLSIDFromString(kWPDDevInterfaceGUID, &guidDevInterface)))
|
| + return false;
|
| + DEV_BROADCAST_DEVICEINTERFACE* dev_interface =
|
| + reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data);
|
| + return (IsEqualGUID(dev_interface->dbcc_classguid, guidDevInterface) != 0);
|
| +}
|
| +
|
| +// Returns the portable device plug and play device ID string.
|
| +string16 GetPnpDeviceId(LPARAM data) {
|
| + DEV_BROADCAST_DEVICEINTERFACE* dev_interface =
|
| + reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data);
|
| + if (!dev_interface)
|
| + return string16();
|
| + string16 device_id(dev_interface->dbcc_name);
|
| + DCHECK(IsStringASCII(device_id));
|
| + return StringToLowerASCII(device_id);
|
| +}
|
| +
|
| +// Gets the friendly name of the device specified by the |pnp_device_id|. On
|
| +// success, returns true and fills in |name|.
|
| +bool GetFriendlyName(const string16& pnp_device_id,
|
| + IPortableDeviceManager* device_manager,
|
| + string16* name) {
|
| + DCHECK(device_manager);
|
| + DCHECK(name);
|
| + DWORD name_len = 0;
|
| + HRESULT hr = device_manager->GetDeviceFriendlyName(pnp_device_id.c_str(),
|
| + NULL, &name_len);
|
| + if (FAILED(hr))
|
| + return false;
|
| +
|
| + hr = device_manager->GetDeviceFriendlyName(
|
| + pnp_device_id.c_str(), WriteInto(name, name_len), &name_len);
|
| + return (SUCCEEDED(hr) && !name->empty());
|
| +}
|
| +
|
| +// Gets the manufacturer name of the device specified by the |pnp_device_id|.
|
| +// On success, returns true and fills in |name|.
|
| +bool GetManufacturerName(const string16& pnp_device_id,
|
| + IPortableDeviceManager* device_manager,
|
| + string16* name) {
|
| + DCHECK(device_manager);
|
| + DCHECK(name);
|
| + DWORD name_len = 0;
|
| + HRESULT hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(),
|
| + NULL, &name_len);
|
| + if (FAILED(hr))
|
| + return false;
|
| +
|
| + hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(),
|
| + WriteInto(name, name_len),
|
| + &name_len);
|
| + return (SUCCEEDED(hr) && !name->empty());
|
| +}
|
| +
|
| +// Gets the description of the device specified by the |pnp_device_id|. On
|
| +// success, returns true and fills in |description|.
|
| +bool GetDeviceDescription(const string16& pnp_device_id,
|
| + IPortableDeviceManager* device_manager,
|
| + string16* description) {
|
| + DCHECK(device_manager);
|
| + DCHECK(description);
|
| + DWORD desc_len = 0;
|
| + HRESULT hr = device_manager->GetDeviceDescription(pnp_device_id.c_str(), NULL,
|
| + &desc_len);
|
| + if (FAILED(hr))
|
| + return false;
|
| +
|
| + hr = device_manager->GetDeviceDescription(pnp_device_id.c_str(),
|
| + WriteInto(description, desc_len),
|
| + &desc_len);
|
| + return (SUCCEEDED(hr) && !description->empty());
|
| +}
|
| +
|
| +// On success, returns true and updates |client_info| with a reference to an
|
| +// IPortableDeviceValues interface that holds information about the
|
| +// application that communicates with the device.
|
| +bool GetClientInformation(
|
| + base::win::ScopedComPtr<IPortableDeviceValues>* client_info) {
|
| + HRESULT hr = client_info->CreateInstance(__uuidof(PortableDeviceValues),
|
| + NULL, CLSCTX_INPROC_SERVER);
|
| + if (FAILED(hr)) {
|
| + DPLOG(ERROR) << "Failed to create an instance of IPortableDeviceValues";
|
| + return false;
|
| + }
|
| +
|
| + // Attempt to set client details.
|
| + (*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;
|
| +}
|
| +
|
| +// Opens the device for communication. |pnp_device_id| specifies the plug and
|
| +// play device ID string. On success, returns true and updates |device| with a
|
| +// reference to the portable device interface.
|
| +bool SetUp(const string16& pnp_device_id,
|
| + base::win::ScopedComPtr<IPortableDevice>* device) {
|
| + base::win::ScopedComPtr<IPortableDeviceValues> client_info;
|
| + if (!GetClientInformation(&client_info))
|
| + return false;
|
| +
|
| + HRESULT hr = device->CreateInstance(__uuidof(PortableDevice), NULL,
|
| + CLSCTX_INPROC_SERVER);
|
| + if (FAILED(hr)) {
|
| + DPLOG(ERROR) << "Failed to create an instance of IPortableDevice";
|
| + return false;
|
| + }
|
| +
|
| + hr = (*device)->Open(pnp_device_id.c_str(), client_info.get());
|
| + if (SUCCEEDED(hr))
|
| + return true;
|
| +
|
| + if (hr == E_ACCESSDENIED)
|
| + DPLOG(ERROR) << "Access denied to open the device";
|
| + return false;
|
| +}
|
| +
|
| +// Returns the unique id property key of the object specified by the
|
| +// |object_id|.
|
| +REFPROPERTYKEY GetUniqueIdPropertyKey(const string16& object_id) {
|
| + return (object_id == WPD_DEVICE_OBJECT_ID) ?
|
| + WPD_DEVICE_SERIAL_NUMBER : WPD_OBJECT_PERSISTENT_UNIQUE_ID;
|
| +}
|
| +
|
| +// On success, returns true and populates |properties_to_read| with the
|
| +// property key of the object specified by the |object_id|.
|
| +bool PopulatePropertyKeyCollection(
|
| + const string16& object_id,
|
| + base::win::ScopedComPtr<IPortableDeviceKeyCollection>* properties_to_read) {
|
| + HRESULT hr = properties_to_read->CreateInstance(
|
| + __uuidof(PortableDeviceKeyCollection), NULL, CLSCTX_INPROC_SERVER);
|
| + if (FAILED(hr)) {
|
| + DPLOG(ERROR) << "Failed to create IPortableDeviceKeyCollection instance";
|
| + return false;
|
| + }
|
| + REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id);
|
| + hr = (*properties_to_read)->Add(key);
|
| + return SUCCEEDED(hr);
|
| +}
|
| +
|
| +// Wrapper function to get content property string value.
|
| +bool GetStringPropertyValue(IPortableDeviceValues* properties_values,
|
| + REFPROPERTYKEY key,
|
| + string16* value) {
|
| + DCHECK(properties_values);
|
| + DCHECK(value);
|
| + base::win::ScopedCoMem<char16> buffer;
|
| + HRESULT hr = properties_values->GetStringValue(key, &buffer);
|
| + if (FAILED(hr))
|
| + return false;
|
| + *value = static_cast<const char16*>(buffer);
|
| + return true;
|
| +}
|
| +
|
| +// Constructs a unique identifier for the object specified by the |object_id|.
|
| +// On success, returns true and fills in |unique_id|.
|
| +bool GetObjectUniqueId(IPortableDevice* device,
|
| + const string16& object_id,
|
| + string16* unique_id) {
|
| + DCHECK(device);
|
| + DCHECK(unique_id);
|
| + base::win::ScopedComPtr<IPortableDeviceContent> content;
|
| + HRESULT hr = device->Content(content.Receive());
|
| + if (FAILED(hr)) {
|
| + DPLOG(ERROR) << "Failed to get IPortableDeviceContent interface";
|
| + return false;
|
| + }
|
| +
|
| + base::win::ScopedComPtr<IPortableDeviceProperties> properties;
|
| + hr = content->Properties(properties.Receive());
|
| + if (FAILED(hr)) {
|
| + DPLOG(ERROR) << "Failed to get IPortableDeviceProperties interface";
|
| + return false;
|
| + }
|
| +
|
| + base::win::ScopedComPtr<IPortableDeviceKeyCollection> properties_to_read;
|
| + if (!PopulatePropertyKeyCollection(object_id, &properties_to_read))
|
| + return false;
|
| +
|
| + base::win::ScopedComPtr<IPortableDeviceValues> properties_values;
|
| + if (FAILED(properties->GetValues(object_id.c_str(),
|
| + properties_to_read.get(),
|
| + properties_values.Receive()))) {
|
| + return false;
|
| + }
|
| +
|
| + REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id);
|
| + return GetStringPropertyValue(properties_values.get(), key, unique_id);
|
| +}
|
| +
|
| +// Constructs the device storage unique identifier using |device_serial_num| and
|
| +// |storage_id|. On success, returns true and fills in |device_storage_id|.
|
| +bool ConstructDeviceStorageUniqueId(const string16& device_serial_num,
|
| + const string16& storage_id,
|
| + std::string* device_storage_id) {
|
| + if (device_serial_num.empty() && storage_id.empty())
|
| + return false;
|
| +
|
| + DCHECK(device_storage_id);
|
| + *device_storage_id = MediaStorageUtil::MakeDeviceId(
|
| + MediaStorageUtil::MTP_OR_PTP,
|
| + UTF16ToUTF8(storage_id + L':' + device_serial_num));
|
| + return true;
|
| +}
|
| +
|
| +// Gets a list of removable storage object identifiers present in |device|.
|
| +// On success, returns true and fills in |storage_object_ids|.
|
| +bool GetRemovableStorageObjectIds(
|
| + IPortableDevice* device,
|
| + PortableDeviceWatcherWin::StorageObjectIDs* storage_object_ids) {
|
| + DCHECK(device);
|
| + DCHECK(storage_object_ids);
|
| + base::win::ScopedComPtr<IPortableDeviceCapabilities> capabilities;
|
| + HRESULT hr = device->Capabilities(capabilities.Receive());
|
| + if (FAILED(hr)) {
|
| + DPLOG(ERROR) << "Failed to get IPortableDeviceCapabilities interface";
|
| + return false;
|
| + }
|
| +
|
| + base::win::ScopedComPtr<IPortableDevicePropVariantCollection> storage_ids;
|
| + hr = capabilities->GetFunctionalObjects(WPD_FUNCTIONAL_CATEGORY_STORAGE,
|
| + storage_ids.Receive());
|
| + if (FAILED(hr)) {
|
| + DPLOG(ERROR) << "Failed to get IPortableDevicePropVariantCollection";
|
| + return false;
|
| + }
|
| +
|
| + DWORD num_storage_obj_ids = 0;
|
| + hr = storage_ids->GetCount(&num_storage_obj_ids);
|
| + if (FAILED(hr))
|
| + return false;
|
| +
|
| + for (DWORD index = 0; index < num_storage_obj_ids; ++index) {
|
| + PROPVARIANT object_id = {0};
|
| + PropVariantInit(&object_id);
|
| + hr = storage_ids->GetAt(index, &object_id);
|
| + if (SUCCEEDED(hr) && (object_id.pwszVal != NULL) &&
|
| + (object_id.vt == VT_LPWSTR)) {
|
| + storage_object_ids->push_back(object_id.pwszVal);
|
| + }
|
| + PropVariantClear(&object_id);
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +// Returns true if the portable device is mounted on a volume. |device_name|
|
| +// specifies the name of the device.
|
| +bool IsVolumeMountedPortableDevice(const string16& device_name) {
|
| + // If the device is a volume mounted device, |device_name| will be
|
| + // the volume name.
|
| + return ((device_name.length() >= 2) && (device_name[1] == L':') &&
|
| + (((device_name[0] >= L'A') && (device_name[0] <= L'Z')) ||
|
| + ((device_name[0] >= L'a') && (device_name[0] <= L'z'))));
|
| +}
|
| +
|
| +// Returns the name of the device specified by |pnp_device_id|.
|
| +string16 GetDeviceNameOnBlockingThread(
|
| + IPortableDeviceManager* portable_device_manager,
|
| + const string16& pnp_device_id) {
|
| + DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
|
| + DCHECK(portable_device_manager);
|
| + string16 name;
|
| + GetFriendlyName(pnp_device_id, portable_device_manager, &name) ||
|
| + GetDeviceDescription(pnp_device_id, portable_device_manager, &name) ||
|
| + GetManufacturerName(pnp_device_id, portable_device_manager, &name);
|
| + return name;
|
| +}
|
| +
|
| +// Access the device and gets the device storage details. On success, returns
|
| +// true and populates |storage_objects| with device storage details.
|
| +bool GetDeviceStorageObjectsOnBlockingThread(
|
| + const string16& pnp_device_id,
|
| + PortableDeviceWatcherWin::StorageObjects* storage_objects) {
|
| + DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
|
| + DCHECK(storage_objects);
|
| + base::win::ScopedComPtr<IPortableDevice> device;
|
| + if (!SetUp(pnp_device_id, &device))
|
| + return false;
|
| +
|
| + string16 device_serial_num;
|
| + if (!GetObjectUniqueId(device.get(), WPD_DEVICE_OBJECT_ID,
|
| + &device_serial_num)) {
|
| + return false;
|
| + }
|
| +
|
| + PortableDeviceWatcherWin::StorageObjectIDs storage_obj_ids;
|
| + if (!GetRemovableStorageObjectIds(device.get(), &storage_obj_ids))
|
| + return false;
|
| + for (PortableDeviceWatcherWin::StorageObjectIDs::const_iterator id_iter =
|
| + storage_obj_ids.begin(); id_iter != storage_obj_ids.end(); ++id_iter) {
|
| + string16 storage_persistent_id;
|
| + if (!GetObjectUniqueId(device.get(), *id_iter, &storage_persistent_id))
|
| + continue;
|
| +
|
| + std::string device_storage_id;
|
| + if (ConstructDeviceStorageUniqueId(device_serial_num, storage_persistent_id,
|
| + &device_storage_id)) {
|
| + storage_objects->push_back(PortableDeviceWatcherWin::DeviceStorageObject(
|
| + *id_iter, device_storage_id));
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +// Accesses the device and gets the device details (name, storage info, etc).
|
| +// On success returns true and fills in |device_details|. On failure, returns
|
| +// false. |pnp_device_id| specifies the plug and play device ID string.
|
| +bool GetDeviceInfoOnBlockingThread(
|
| + IPortableDeviceManager* portable_device_manager,
|
| + const string16& pnp_device_id,
|
| + PortableDeviceWatcherWin::DeviceDetails* device_details) {
|
| + DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
|
| + DCHECK(portable_device_manager);
|
| + DCHECK(device_details);
|
| + DCHECK(!pnp_device_id.empty());
|
| + device_details->name = GetDeviceNameOnBlockingThread(portable_device_manager,
|
| + pnp_device_id);
|
| + if (IsVolumeMountedPortableDevice(device_details->name))
|
| + return false;
|
| +
|
| + device_details->location = pnp_device_id;
|
| + PortableDeviceWatcherWin::StorageObjects storage_objects;
|
| + return GetDeviceStorageObjectsOnBlockingThread(
|
| + pnp_device_id, &device_details->storage_objects);
|
| +}
|
| +
|
| +// Wrapper function to get an instance of portable device manager. On success,
|
| +// returns true and fills in |portable_device_mgr|. On failure, returns false.
|
| +bool GetPortableDeviceManager(
|
| + base::win::ScopedComPtr<IPortableDeviceManager>* portable_device_mgr) {
|
| + DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
|
| + HRESULT hr = portable_device_mgr->CreateInstance(
|
| + __uuidof(PortableDeviceManager), NULL, CLSCTX_INPROC_SERVER);
|
| + if (SUCCEEDED(hr))
|
| + return true;
|
| +
|
| + // Either there is no portable device support (Windows XP with old versions of
|
| + // Media Player) or the thread does not have COM initialized.
|
| + DCHECK_NE(CO_E_NOTINITIALIZED, hr);
|
| + return false;
|
| +}
|
| +
|
| +// Enumerates the attached portable devices. On success, returns true and fills
|
| +// in |devices| with the attached portable device details. On failure, returns
|
| +// false.
|
| +bool EnumerateAttachedDevicesOnBlockingThread(
|
| + PortableDeviceWatcherWin::Devices* devices) {
|
| + DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
|
| + DCHECK(devices);
|
| + base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr;
|
| + if (!GetPortableDeviceManager(&portable_device_mgr))
|
| + return false;
|
| +
|
| + // Get the total number of devices found on the system.
|
| + DWORD pnp_device_count = 0;
|
| + HRESULT hr = portable_device_mgr->GetDevices(NULL, &pnp_device_count);
|
| + if (FAILED(hr))
|
| + return false;
|
| +
|
| + scoped_array<char16*> pnp_device_ids(new char16*[pnp_device_count]);
|
| + ZeroMemory(pnp_device_ids.get(), pnp_device_count);
|
| + hr = portable_device_mgr->GetDevices(pnp_device_ids.get(), &pnp_device_count);
|
| + if (FAILED(hr))
|
| + return false;
|
| +
|
| + for (DWORD index = 0; index < pnp_device_count; ++index) {
|
| + PortableDeviceWatcherWin::DeviceDetails device_details;
|
| + if (GetDeviceInfoOnBlockingThread(
|
| + portable_device_mgr, pnp_device_ids[index], &device_details))
|
| + devices->push_back(device_details);
|
| + }
|
| + return !devices->empty();
|
| +}
|
| +
|
| +// Handles the device attach event message on a media task runner.
|
| +// |pnp_device_id| specifies the attached plug and play device ID string. On
|
| +// success, returns true and populates |device_details| with device information.
|
| +// On failure, returns false.
|
| +bool HandleDeviceAttachedEventOnBlockingThread(
|
| + const string16& pnp_device_id,
|
| + PortableDeviceWatcherWin::DeviceDetails* device_details) {
|
| + DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
|
| + DCHECK(device_details);
|
| + base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr;
|
| + if (!GetPortableDeviceManager(&portable_device_mgr))
|
| + return false;
|
| + // Sometimes, portable device manager doesn't have the new device details.
|
| + // Refresh the manager device list to update its details.
|
| + portable_device_mgr->RefreshDeviceList();
|
| + return GetDeviceInfoOnBlockingThread(portable_device_mgr, pnp_device_id,
|
| + device_details);
|
| +}
|
| +
|
| +// Constructs and returns a storage path from storage unique identifier.
|
| +string16 GetStoragePathFromStorageId(const std::string& storage_unique_id) {
|
| + // Construct a dummy device path using the storage name. This is only used
|
| + // for registering the device media file system.
|
| + DCHECK(!storage_unique_id.empty());
|
| + return UTF8ToUTF16("\\\\" + storage_unique_id);
|
| +}
|
| +
|
| +// Registers |hwnd| to receive portable device notification details. On success,
|
| +// returns the device notifications handle else returns NULL.
|
| +HDEVNOTIFY RegisterPortableDeviceNotification(HWND hwnd) {
|
| + GUID dev_interface_guid = GUID_NULL;
|
| + HRESULT hr = CLSIDFromString(kWPDDevInterfaceGUID, &dev_interface_guid);
|
| + if (FAILED(hr))
|
| + return NULL;
|
| + DEV_BROADCAST_DEVICEINTERFACE db = {
|
| + sizeof(DEV_BROADCAST_DEVICEINTERFACE),
|
| + DBT_DEVTYP_DEVICEINTERFACE,
|
| + 0,
|
| + dev_interface_guid
|
| + };
|
| + return RegisterDeviceNotification(hwnd, &db, DEVICE_NOTIFY_WINDOW_HANDLE);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +
|
| +// PortableDeviceWatcherWin ---------------------------------------------------
|
| +
|
| +PortableDeviceWatcherWin::DeviceStorageObject::DeviceStorageObject(
|
| + const string16& temporary_id,
|
| + const std::string& persistent_id)
|
| + : object_temporary_id(temporary_id),
|
| + object_persistent_id(persistent_id) {
|
| +}
|
| +
|
| +PortableDeviceWatcherWin::PortableDeviceWatcherWin()
|
| + : notifications_(NULL),
|
| + ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
|
| +}
|
| +
|
| +PortableDeviceWatcherWin::~PortableDeviceWatcherWin() {
|
| + UnregisterDeviceNotification(notifications_);
|
| +}
|
| +
|
| +void PortableDeviceWatcherWin::Init(HWND hwnd) {
|
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
| + notifications_ = RegisterPortableDeviceNotification(hwnd);
|
| + base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
|
| + media_task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior(
|
| + pool->GetNamedSequenceToken(kMediaTaskRunnerName),
|
| + base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
|
| + EnumerateAttachedDevices();
|
| +}
|
| +
|
| +void PortableDeviceWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) {
|
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
| + if (!IsPortableDeviceStructure(data))
|
| + return;
|
| +
|
| + string16 device_id = GetPnpDeviceId(data);
|
| + if (event_type == DBT_DEVICEARRIVAL)
|
| + HandleDeviceAttachEvent(device_id);
|
| + else if (event_type == DBT_DEVICEREMOVECOMPLETE)
|
| + HandleDeviceDetachEvent(device_id);
|
| +}
|
| +
|
| +void PortableDeviceWatcherWin::EnumerateAttachedDevices() {
|
| + DCHECK(media_task_runner_.get());
|
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
| + Devices* devices = new Devices;
|
| + base::PostTaskAndReplyWithResult(
|
| + media_task_runner_,
|
| + FROM_HERE,
|
| + base::Bind(&EnumerateAttachedDevicesOnBlockingThread, devices),
|
| + base::Bind(&PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices,
|
| + weak_ptr_factory_.GetWeakPtr(), base::Owned(devices)));
|
| +}
|
| +
|
| +void PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices(
|
| + const Devices* devices, const bool result) {
|
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
| + DCHECK(devices);
|
| + if (!result)
|
| + return;
|
| + for (Devices::const_iterator device_iter = devices->begin();
|
| + device_iter != devices->end(); ++device_iter) {
|
| + OnDidHandleDeviceAttachEvent(&(*device_iter), result);
|
| + }
|
| +}
|
| +
|
| +void PortableDeviceWatcherWin::HandleDeviceAttachEvent(
|
| + const string16& pnp_device_id) {
|
| + DCHECK(media_task_runner_.get());
|
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
| + DeviceDetails* device_details = new DeviceDetails;
|
| + base::PostTaskAndReplyWithResult(
|
| + media_task_runner_,
|
| + FROM_HERE,
|
| + base::Bind(&HandleDeviceAttachedEventOnBlockingThread, pnp_device_id,
|
| + device_details),
|
| + base::Bind(&PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent,
|
| + weak_ptr_factory_.GetWeakPtr(), base::Owned(device_details)));
|
| +}
|
| +
|
| +void PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent(
|
| + const DeviceDetails* device_details, const bool result) {
|
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
| + DCHECK(device_details);
|
| + if (!result)
|
| + return;
|
| +
|
| + const StorageObjects& storage_objects = device_details->storage_objects;
|
| + const string16& name = device_details->name;
|
| + const string16& location = device_details->location;
|
| + DCHECK(!ContainsKey(device_map_, location));
|
| + base::SystemMonitor* system_monitor = base::SystemMonitor::Get();
|
| + DCHECK(system_monitor);
|
| + for (StorageObjects::const_iterator storage_iter = storage_objects.begin();
|
| + storage_iter != storage_objects.end(); ++storage_iter) {
|
| + const std::string& storage_id = storage_iter->object_persistent_id;
|
| + DCHECK(!ContainsKey(storage_map_, storage_id));
|
| +
|
| + // Keep track of storage id and storage name to see how often we receive
|
| + // empty values.
|
| + MediaStorageUtil::RecordDeviceInfoHistogram(false, storage_id, name);
|
| + if (storage_id.empty() || name.empty())
|
| + return;
|
| +
|
| + // Device can have several data partitions. Therefore, add the
|
| + // partition identifier to the storage name. E.g.: "Nexus 7 (s10001)"
|
| + string16 storage_name(name + L" (" + storage_iter->object_temporary_id +
|
| + L')');
|
| + storage_map_[storage_id] =
|
| + base::SystemMonitor::RemovableStorageInfo(storage_id, storage_name,
|
| + location);
|
| + system_monitor->ProcessRemovableStorageAttached(
|
| + storage_id, storage_name, GetStoragePathFromStorageId(storage_id));
|
| + }
|
| + device_map_[location] = storage_objects;
|
| +}
|
| +
|
| +void PortableDeviceWatcherWin::HandleDeviceDetachEvent(
|
| + const string16& pnp_device_id) {
|
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
| + MTPDeviceMap::iterator device_iter = device_map_.find(pnp_device_id);
|
| + if (device_iter == device_map_.end())
|
| + return;
|
| +
|
| + base::SystemMonitor* system_monitor = base::SystemMonitor::Get();
|
| + DCHECK(system_monitor);
|
| +
|
| + const StorageObjects& storage_objects = device_iter->second;
|
| + for (StorageObjects::const_iterator storage_object_iter =
|
| + storage_objects.begin(); storage_object_iter != storage_objects.end();
|
| + ++storage_object_iter) {
|
| + std::string storage_id = storage_object_iter->object_persistent_id;
|
| + MTPStorageMap::iterator storage_map_iter = storage_map_.find(storage_id);
|
| + DCHECK(storage_map_iter != storage_map_.end());
|
| + system_monitor->ProcessRemovableStorageDetached(
|
| + storage_map_iter->second.device_id);
|
| + storage_map_.erase(storage_map_iter);
|
| + }
|
| + device_map_.erase(device_iter);
|
| +}
|
| +
|
| +} // namespace chrome
|
|
|