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

Unified Diff: chrome/browser/system_monitor/portable_device_watcher_win.cc

Issue 11088012: [Win, MediaGallery] Enumerate and handle mtp device attach/detach events. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rename storage_iter => storage_map_iter 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/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

Powered by Google App Engine
This is Rietveld 408576698