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..4b72131db1018c14b61c0d411ca501ded4ffb018 |
--- /dev/null |
+++ b/chrome/browser/system_monitor/portable_device_watcher_win.cc |
@@ -0,0 +1,620 @@ |
+// 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/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 "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"; |
+ |
+// 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(); |
+ DCHECK(IsStringASCII(dev_interface->dbcc_name)); |
+ return StringToLowerASCII(string16(dev_interface->dbcc_name)); |
+} |
+ |
+// 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() : false; |
+} |
+ |
+// 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() : false; |
+} |
+ |
+// 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() : false; |
+} |
+ |
+// 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 wchar_t*>(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); |
+ string16 unique_id(chrome::kMtpDeviceStorageIdPrefix + storage_id + L':' + |
vandebo (ex-Chrome)
2012/10/26 20:32:10
If there's only one format of device ids for this
kmadhusu
2012/10/26 22:23:26
Done.
|
+ device_serial_num); |
+ |
+ *device_storage_id = MediaStorageUtil::MakeDeviceId( |
+ MediaStorageUtil::MTP_OR_PTP, UTF16ToUTF8(unique_id)); |
+ return true; |
+} |
+ |
+// Gets a list of removable storage object identifiers present in |device|. |
+// On success, returns true and fills in |storage_ids|. |
+bool GetRemovableStorageObjectIds(IPortableDevice* device, |
+ std::vector<string16>* storage_ids) { |
+ DCHECK(device); |
+ DCHECK(storage_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_obj_ids; |
+ hr = capabilities->GetFunctionalObjects(WPD_FUNCTIONAL_CATEGORY_STORAGE, |
+ storage_obj_ids.Receive()); |
+ if (FAILED(hr)) { |
+ DPLOG(ERROR) << "Failed to get IPortableDevicePropVariantCollection"; |
+ return false; |
+ } |
+ |
+ DWORD num_storage_obj_ids = 0; |
+ hr = storage_obj_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_obj_ids->GetAt(index, &object_id); |
+ if (SUCCEEDED(hr) && (object_id.pwszVal != NULL) && |
+ (object_id.vt == VT_LPWSTR)) { |
+ storage_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|. |
+// Since this task may take a longer time to complete, this function is |
+// accessed via the media task runner. |
+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. Since this task may |
+// take a longer time to complete, this function is accessed via the media task |
+// runner. 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; |
+ } |
+ |
+ std::vector<string16> storage_obj_ids; |
+ if (!GetRemovableStorageObjectIds(device.get(), &storage_obj_ids)) |
+ return false; |
+ |
+ for (std::vector<string16>::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)) { |
+ PortableDeviceWatcherWin::DeviceStorageObject storage_object; |
+ storage_object.object_temporary_id = *id_iter; |
+ storage_object.object_persistent_id = device_storage_id; |
+ storage_objects->push_back(storage_object); |
+ } |
+ } |
+ return true; |
+} |
+ |
+// Access the device and gets the device details (name, storage info, etc.,). |
+// Since this task may take a longer time to complete, this function is |
+// accessed via the media task runner. On success returns true and fills in |
+// |device_details|. |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. This function |
+// may take a long time to complete. Therefore, access this function via a media |
+// task runner. On success, returns true and fills in |portable_device_mgr|. |
+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 (it must be a XP with Windows |
+ // Media Player Version < 10) or the thread does not have COM initialized. |
+ DCHECK_NE(CO_E_NOTINITIALIZED, hr); |
+ return false; |
+} |
+ |
+// Enumerates the attached portable devices. This function accesses the device |
+// to get the device details. Since this task may take a longer time to |
+// complete, this function is accessed via the media task runner. On success, |
+// fills in |device_details_vector| with the attached portable device details. |
+void EnumerateAttachedDevicesOnBlockingThread( |
+ PortableDeviceWatcherWin::DeviceDetailsVector* device_details_vector) { |
+ DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
+ DCHECK(device_details_vector); |
+ base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr; |
+ if (!GetPortableDeviceManager(&portable_device_mgr)) |
+ return; |
+ |
+ // 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; |
+ |
+ scoped_array<LPWSTR> pnp_device_ids(new LPWSTR[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; |
+ |
+ for (DWORD index = 0; index < pnp_device_count; ++index) { |
+ PortableDeviceWatcherWin::DeviceDetails device_details; |
+ if (!GetDeviceInfoOnBlockingThread(portable_device_mgr, |
+ pnp_device_ids[index], |
+ &device_details)) { |
+ continue; |
+ } |
+ device_details_vector->push_back(device_details); |
+ } |
+} |
+ |
+// Handles the device attach event message on a media task runner. This |
+// function communicates with the device to get the storage details. This task |
+// may take a longer time. Therefore, access this function on a media task |
+// runner. |pnp_device_id| specifies the attached plug and play device ID |
+// string. On success, populates |device_details| with device information. |
+void 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; |
+ portable_device_mgr->RefreshDeviceList(); |
+ 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()); |
+ const std::string root_path("\\\\"); |
+ return UTF8ToUTF16(root_path + storage_unique_id); |
+} |
+ |
+} // namespace |
+ |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+// PortableDeviceWatcherWin implementation // |
+/////////////////////////////////////////////////////////////////////////////// |
+PortableDeviceWatcherWin::PortableDeviceWatcherWin() |
+ : app_terminating_(false), |
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { |
+ registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, |
+ content::NotificationService::AllSources()); |
+} |
+ |
+PortableDeviceWatcherWin::~PortableDeviceWatcherWin() { |
+} |
+ |
+void PortableDeviceWatcherWin::Init() { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ if (app_terminating_) |
+ return; |
+ |
+ base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); |
+ base::SequencedWorkerPool::SequenceToken media_sequence_token = |
+ pool->GetNamedSequenceToken(kMediaTaskRunnerName); |
+ media_task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior( |
+ media_sequence_token, base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); |
+ EnumerateAttachedDevices(); |
+} |
+ |
+void PortableDeviceWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ if (app_terminating_) |
+ return; |
+ |
+ 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)); |
+ if (app_terminating_) |
+ return; |
+ DeviceDetailsVector* device_details_vector = new DeviceDetailsVector; |
+ media_task_runner_->PostTaskAndReply( |
+ FROM_HERE, |
+ base::Bind(&EnumerateAttachedDevicesOnBlockingThread, |
+ base::Unretained(device_details_vector)), |
vandebo (ex-Chrome)
2012/10/26 20:32:10
nit: Unretained not needed
kmadhusu
2012/10/26 22:23:26
Removed.
|
+ base::Bind(&PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ base::Owned(device_details_vector))); |
+} |
+ |
+void PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices( |
+ const DeviceDetailsVector* device_details_vector) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ DCHECK(device_details_vector); |
+ for (DeviceDetailsVector::const_iterator it = device_details_vector->begin(); |
+ it != device_details_vector->end(); ++it) { |
+ OnDidHandleDeviceAttachEvent(&(*it)); |
+ } |
+} |
+ |
+void PortableDeviceWatcherWin::HandleDeviceAttachEvent( |
+ const string16& pnp_device_id) { |
+ DCHECK(media_task_runner_.get()); |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ if (app_terminating_) |
+ return; |
+ DeviceDetails* device_details = new DeviceDetails; |
+ media_task_runner_->PostTaskAndReply( |
+ FROM_HERE, |
+ base::Bind(&HandleDeviceAttachedEventOnBlockingThread, pnp_device_id, |
+ base::Unretained(device_details)), |
+ base::Bind(&PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ base::Owned(device_details))); |
+} |
+ |
+void PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent( |
+ const DeviceDetails* device_details) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ DCHECK(device_details); |
+ if (app_terminating_) |
+ 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_entry = device_map_.find(pnp_device_id); |
+ if (device_entry == device_map_.end()) |
+ return; |
+ |
+ base::SystemMonitor* system_monitor = base::SystemMonitor::Get(); |
+ DCHECK(system_monitor); |
+ |
+ StorageObjects storage_objects = device_entry->second; |
+ for (StorageObjects::const_iterator storage_iter = storage_objects.begin(); |
+ storage_iter != storage_objects.end(); ++storage_iter) { |
+ std::string storage_id = storage_iter->object_persistent_id; |
+ MtpStorageMap::iterator storage_entry = storage_map_.find(storage_id); |
+ DCHECK(storage_entry != storage_map_.end()); |
+ system_monitor->ProcessRemovableStorageDetached( |
+ storage_entry->second.device_id); |
+ storage_map_.erase(storage_entry); |
+ } |
+ device_map_.erase(device_entry); |
+} |
+ |
+void PortableDeviceWatcherWin::Observe( |
+ int type, |
+ const content::NotificationSource& source, |
+ const content::NotificationDetails& details) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type); |
+ app_terminating_ = true; |
+} |
+ |
+} // namespace chrome |