15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Any tasks that communicates with the portable device may take >100ms to
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// complete. Those tasks should be run on an blocking thread instead of the
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// UI thread.
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/storage_monitor/portable_device_watcher_win.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <dbt.h>
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <portabledevice.h>
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stl_util.h"
17868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
18868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/threading/sequenced_worker_pool.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/scoped_co_mem.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/scoped_comptr.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/win/scoped_propvariant.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/storage_monitor/media_storage_util.h"
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/storage_monitor/removable_device_constants.h"
2590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/storage_monitor/storage_info.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace chrome {
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Name of the client application that communicates with the MTP device.
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char16 kClientName[] = L"Chromium";
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Name of the sequenced task runner.
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kMediaTaskRunnerName[] = "media-task-runner";
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns true if |data| represents a class of portable devices.
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool IsPortableDeviceStructure(LPARAM data) {
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DEV_BROADCAST_HDR* broadcast_hdr =
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      reinterpret_cast<DEV_BROADCAST_HDR*>(data);
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!broadcast_hdr ||
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (broadcast_hdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)) {
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GUID guidDevInterface = GUID_NULL;
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(CLSIDFromString(kWPDDevInterfaceGUID, &guidDevInterface)))
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DEV_BROADCAST_DEVICEINTERFACE* dev_interface =
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data);
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (IsEqualGUID(dev_interface->dbcc_classguid, guidDevInterface) != 0);
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns the portable device plug and play device ID string.
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 GetPnpDeviceId(LPARAM data) {
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DEV_BROADCAST_DEVICEINTERFACE* dev_interface =
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(data);
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!dev_interface)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return string16();
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 device_id(dev_interface->dbcc_name);
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(IsStringASCII(device_id));
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return StringToLowerASCII(device_id);
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Gets the friendly name of the device specified by the |pnp_device_id|. On
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// success, returns true and fills in |name|.
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool GetFriendlyName(const string16& pnp_device_id,
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     IPortableDeviceManager* device_manager,
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     string16* name) {
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(device_manager);
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(name);
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD name_len = 0;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HRESULT hr = device_manager->GetDeviceFriendlyName(pnp_device_id.c_str(),
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                     NULL, &name_len);
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr))
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  hr = device_manager->GetDeviceFriendlyName(
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pnp_device_id.c_str(), WriteInto(name, name_len), &name_len);
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (SUCCEEDED(hr) && !name->empty());
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Gets the manufacturer name of the device specified by the |pnp_device_id|.
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// On success, returns true and fills in |name|.
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool GetManufacturerName(const string16& pnp_device_id,
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         IPortableDeviceManager* device_manager,
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         string16* name) {
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(device_manager);
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(name);
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD name_len = 0;
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HRESULT hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(),
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                     NULL, &name_len);
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr))
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  hr = device_manager->GetDeviceManufacturer(pnp_device_id.c_str(),
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             WriteInto(name, name_len),
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             &name_len);
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (SUCCEEDED(hr) && !name->empty());
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Gets the description of the device specified by the |pnp_device_id|. On
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// success, returns true and fills in |description|.
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool GetDeviceDescription(const string16& pnp_device_id,
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          IPortableDeviceManager* device_manager,
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          string16* description) {
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(device_manager);
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(description);
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD desc_len = 0;
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HRESULT hr = device_manager->GetDeviceDescription(pnp_device_id.c_str(), NULL,
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                    &desc_len);
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr))
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  hr = device_manager->GetDeviceDescription(pnp_device_id.c_str(),
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            WriteInto(description, desc_len),
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            &desc_len);
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (SUCCEEDED(hr) && !description->empty());
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// On success, returns true and updates |client_info| with a reference to an
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// IPortableDeviceValues interface that holds information about the
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// application that communicates with the device.
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool GetClientInformation(
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::win::ScopedComPtr<IPortableDeviceValues>* client_info) {
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HRESULT hr = client_info->CreateInstance(__uuidof(PortableDeviceValues),
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           NULL, CLSCTX_INPROC_SERVER);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr)) {
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DPLOG(ERROR) << "Failed to create an instance of IPortableDeviceValues";
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Attempt to set client details.
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (*client_info)->SetStringValue(WPD_CLIENT_NAME, kClientName);
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, 0);
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, 0);
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_REVISION, 0);
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (*client_info)->SetUnsignedIntegerValue(
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE, SECURITY_IMPERSONATION);
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (*client_info)->SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS,
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                          GENERIC_READ);
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Opens the device for communication. |pnp_device_id| specifies the plug and
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// play device ID string. On success, returns true and updates |device| with a
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// reference to the portable device interface.
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SetUp(const string16& pnp_device_id,
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           base::win::ScopedComPtr<IPortableDevice>* device) {
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::win::ScopedComPtr<IPortableDeviceValues> client_info;
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetClientInformation(&client_info))
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HRESULT hr = device->CreateInstance(__uuidof(PortableDevice), NULL,
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      CLSCTX_INPROC_SERVER);
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr)) {
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DPLOG(ERROR) << "Failed to create an instance of IPortableDevice";
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  hr = (*device)->Open(pnp_device_id.c_str(), client_info.get());
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (SUCCEEDED(hr))
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (hr == E_ACCESSDENIED)
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DPLOG(ERROR) << "Access denied to open the device";
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns the unique id property key of the object specified by the
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// |object_id|.
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)REFPROPERTYKEY GetUniqueIdPropertyKey(const string16& object_id) {
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (object_id == WPD_DEVICE_OBJECT_ID) ?
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      WPD_DEVICE_SERIAL_NUMBER : WPD_OBJECT_PERSISTENT_UNIQUE_ID;
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// On success, returns true and populates |properties_to_read| with the
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// property key of the object specified by the |object_id|.
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool PopulatePropertyKeyCollection(
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& object_id,
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::win::ScopedComPtr<IPortableDeviceKeyCollection>* properties_to_read) {
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HRESULT hr = properties_to_read->CreateInstance(
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      __uuidof(PortableDeviceKeyCollection), NULL, CLSCTX_INPROC_SERVER);
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr)) {
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DPLOG(ERROR) << "Failed to create IPortableDeviceKeyCollection instance";
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id);
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  hr = (*properties_to_read)->Add(key);
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SUCCEEDED(hr);
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Wrapper function to get content property string value.
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool GetStringPropertyValue(IPortableDeviceValues* properties_values,
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            REFPROPERTYKEY key,
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            string16* value) {
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(properties_values);
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(value);
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::win::ScopedCoMem<char16> buffer;
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HRESULT hr = properties_values->GetStringValue(key, &buffer);
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr))
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *value = static_cast<const char16*>(buffer);
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Constructs a unique identifier for the object specified by the |object_id|.
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// On success, returns true and fills in |unique_id|.
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool GetObjectUniqueId(IPortableDevice* device,
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       const string16& object_id,
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       string16* unique_id) {
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(device);
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(unique_id);
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::win::ScopedComPtr<IPortableDeviceContent> content;
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HRESULT hr = device->Content(content.Receive());
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr)) {
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DPLOG(ERROR) << "Failed to get IPortableDeviceContent interface";
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::win::ScopedComPtr<IPortableDeviceProperties> properties;
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  hr = content->Properties(properties.Receive());
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr)) {
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DPLOG(ERROR) << "Failed to get IPortableDeviceProperties interface";
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::win::ScopedComPtr<IPortableDeviceKeyCollection> properties_to_read;
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!PopulatePropertyKeyCollection(object_id, &properties_to_read))
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::win::ScopedComPtr<IPortableDeviceValues> properties_values;
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(properties->GetValues(object_id.c_str(),
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   properties_to_read.get(),
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   properties_values.Receive()))) {
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  REFPROPERTYKEY key = GetUniqueIdPropertyKey(object_id);
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return GetStringPropertyValue(properties_values.get(), key, unique_id);
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Constructs the device storage unique identifier using |device_serial_num| and
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// |storage_id|. On success, returns true and fills in |device_storage_id|.
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ConstructDeviceStorageUniqueId(const string16& device_serial_num,
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    const string16& storage_id,
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    std::string* device_storage_id) {
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (device_serial_num.empty() && storage_id.empty())
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(device_storage_id);
25390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  *device_storage_id = StorageInfo::MakeDeviceId(
25490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)       StorageInfo::MTP_OR_PTP,
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       UTF16ToUTF8(storage_id + L':' + device_serial_num));
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Gets a list of removable storage object identifiers present in |device|.
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// On success, returns true and fills in |storage_object_ids|.
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool GetRemovableStorageObjectIds(
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IPortableDevice* device,
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PortableDeviceWatcherWin::StorageObjectIDs* storage_object_ids) {
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(device);
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(storage_object_ids);
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::win::ScopedComPtr<IPortableDeviceCapabilities> capabilities;
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HRESULT hr = device->Capabilities(capabilities.Receive());
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr)) {
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DPLOG(ERROR) << "Failed to get IPortableDeviceCapabilities interface";
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::win::ScopedComPtr<IPortableDevicePropVariantCollection> storage_ids;
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  hr = capabilities->GetFunctionalObjects(WPD_FUNCTIONAL_CATEGORY_STORAGE,
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                          storage_ids.Receive());
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr)) {
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DPLOG(ERROR) << "Failed to get IPortableDevicePropVariantCollection";
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD num_storage_obj_ids = 0;
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  hr = storage_ids->GetCount(&num_storage_obj_ids);
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr))
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (DWORD index = 0; index < num_storage_obj_ids; ++index) {
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::win::ScopedPropVariant object_id;
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    hr = storage_ids->GetAt(index, object_id.Receive());
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (SUCCEEDED(hr) && object_id.get().vt == VT_LPWSTR &&
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        object_id.get().pwszVal != NULL) {
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      storage_object_ids->push_back(object_id.get().pwszVal);
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns true if the portable device belongs to a mass storage class.
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// |pnp_device_id| specifies the plug and play device id.
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// |device_name| specifies the name of the device.
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool IsMassStoragePortableDevice(const string16& pnp_device_id,
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 const string16& device_name) {
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Based on testing, if the pnp device id starts with "\\?\wpdbusenumroot#",
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // then the attached device belongs to a mass storage class.
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (StartsWith(pnp_device_id, L"\\\\?\\wpdbusenumroot#", false))
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return true;
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the device is a volume mounted device, |device_name| will be
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the volume name.
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ((device_name.length() >= 2) && (device_name[1] == L':') &&
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (((device_name[0] >= L'A') && (device_name[0] <= L'Z')) ||
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ((device_name[0] >= L'a') && (device_name[0] <= L'z'))));
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns the name of the device specified by |pnp_device_id|.
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 GetDeviceNameOnBlockingThread(
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IPortableDeviceManager* portable_device_manager,
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& pnp_device_id) {
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(portable_device_manager);
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 name;
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetFriendlyName(pnp_device_id, portable_device_manager, &name) ||
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetDeviceDescription(pnp_device_id, portable_device_manager, &name) ||
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetManufacturerName(pnp_device_id, portable_device_manager, &name);
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return name;
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Access the device and gets the device storage details. On success, returns
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// true and populates |storage_objects| with device storage details.
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool GetDeviceStorageObjectsOnBlockingThread(
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& pnp_device_id,
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PortableDeviceWatcherWin::StorageObjects* storage_objects) {
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(storage_objects);
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::win::ScopedComPtr<IPortableDevice> device;
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!SetUp(pnp_device_id, &device))
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 device_serial_num;
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetObjectUniqueId(device.get(), WPD_DEVICE_OBJECT_ID,
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         &device_serial_num)) {
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PortableDeviceWatcherWin::StorageObjectIDs storage_obj_ids;
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetRemovableStorageObjectIds(device.get(), &storage_obj_ids))
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (PortableDeviceWatcherWin::StorageObjectIDs::const_iterator id_iter =
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       storage_obj_ids.begin(); id_iter != storage_obj_ids.end(); ++id_iter) {
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    string16 storage_persistent_id;
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!GetObjectUniqueId(device.get(), *id_iter, &storage_persistent_id))
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string device_storage_id;
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (ConstructDeviceStorageUniqueId(device_serial_num, storage_persistent_id,
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       &device_storage_id)) {
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      storage_objects->push_back(PortableDeviceWatcherWin::DeviceStorageObject(
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          *id_iter, device_storage_id));
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Accesses the device and gets the device details (name, storage info, etc).
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// On success returns true and fills in |device_details|. On failure, returns
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// false. |pnp_device_id| specifies the plug and play device ID string.
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool GetDeviceInfoOnBlockingThread(
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IPortableDeviceManager* portable_device_manager,
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& pnp_device_id,
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PortableDeviceWatcherWin::DeviceDetails* device_details) {
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(portable_device_manager);
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(device_details);
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!pnp_device_id.empty());
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  device_details->name = GetDeviceNameOnBlockingThread(portable_device_manager,
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                       pnp_device_id);
3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (IsMassStoragePortableDevice(pnp_device_id, device_details->name))
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  device_details->location = pnp_device_id;
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PortableDeviceWatcherWin::StorageObjects storage_objects;
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return GetDeviceStorageObjectsOnBlockingThread(
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pnp_device_id, &device_details->storage_objects);
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Wrapper function to get an instance of portable device manager. On success,
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// returns true and fills in |portable_device_mgr|. On failure, returns false.
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool GetPortableDeviceManager(
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::win::ScopedComPtr<IPortableDeviceManager>* portable_device_mgr) {
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HRESULT hr = portable_device_mgr->CreateInstance(
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      __uuidof(PortableDeviceManager), NULL, CLSCTX_INPROC_SERVER);
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (SUCCEEDED(hr))
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Either there is no portable device support (Windows XP with old versions of
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Media Player) or the thread does not have COM initialized.
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(CO_E_NOTINITIALIZED, hr);
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Enumerates the attached portable devices. On success, returns true and fills
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// in |devices| with the attached portable device details. On failure, returns
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// false.
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool EnumerateAttachedDevicesOnBlockingThread(
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PortableDeviceWatcherWin::Devices* devices) {
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(devices);
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr;
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetPortableDeviceManager(&portable_device_mgr))
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get the total number of devices found on the system.
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD pnp_device_count = 0;
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HRESULT hr = portable_device_mgr->GetDevices(NULL, &pnp_device_count);
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr))
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<char16*[]> pnp_device_ids(new char16*[pnp_device_count]);
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  hr = portable_device_mgr->GetDevices(pnp_device_ids.get(), &pnp_device_count);
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr))
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (DWORD index = 0; index < pnp_device_count; ++index) {
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PortableDeviceWatcherWin::DeviceDetails device_details;
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (GetDeviceInfoOnBlockingThread(
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        portable_device_mgr, pnp_device_ids[index], &device_details))
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      devices->push_back(device_details);
4282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    CoTaskMemFree(pnp_device_ids[index]);
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return !devices->empty();
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Handles the device attach event message on a media task runner.
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// |pnp_device_id| specifies the attached plug and play device ID string. On
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// success, returns true and populates |device_details| with device information.
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// On failure, returns false.
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool HandleDeviceAttachedEventOnBlockingThread(
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& pnp_device_id,
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PortableDeviceWatcherWin::DeviceDetails* device_details) {
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(device_details);
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::win::ScopedComPtr<IPortableDeviceManager> portable_device_mgr;
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetPortableDeviceManager(&portable_device_mgr))
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Sometimes, portable device manager doesn't have the new device details.
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Refresh the manager device list to update its details.
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  portable_device_mgr->RefreshDeviceList();
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return GetDeviceInfoOnBlockingThread(portable_device_mgr, pnp_device_id,
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       device_details);
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Registers |hwnd| to receive portable device notification details. On success,
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// returns the device notifications handle else returns NULL.
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HDEVNOTIFY RegisterPortableDeviceNotification(HWND hwnd) {
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GUID dev_interface_guid = GUID_NULL;
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HRESULT hr = CLSIDFromString(kWPDDevInterfaceGUID, &dev_interface_guid);
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr))
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DEV_BROADCAST_DEVICEINTERFACE db = {
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sizeof(DEV_BROADCAST_DEVICEINTERFACE),
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DBT_DEVTYP_DEVICEINTERFACE,
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      0,
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      dev_interface_guid
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return RegisterDeviceNotification(hwnd, &db, DEVICE_NOTIFY_WINDOW_HANDLE);
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// PortableDeviceWatcherWin ---------------------------------------------------
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PortableDeviceWatcherWin::DeviceStorageObject::DeviceStorageObject(
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& temporary_id,
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& persistent_id)
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : object_temporary_id(temporary_id),
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      object_persistent_id(persistent_id) {
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PortableDeviceWatcherWin::PortableDeviceWatcherWin()
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : notifications_(NULL),
4822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      storage_notifications_(NULL),
483c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weak_ptr_factory_(this) {
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PortableDeviceWatcherWin::~PortableDeviceWatcherWin() {
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UnregisterDeviceNotification(notifications_);
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PortableDeviceWatcherWin::Init(HWND hwnd) {
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  notifications_ = RegisterPortableDeviceNotification(hwnd);
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  media_task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior(
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pool->GetNamedSequenceToken(kMediaTaskRunnerName),
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EnumerateAttachedDevices();
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PortableDeviceWatcherWin::OnWindowMessage(UINT event_type, LPARAM data) {
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsPortableDeviceStructure(data))
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 device_id = GetPnpDeviceId(data);
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (event_type == DBT_DEVICEARRIVAL)
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HandleDeviceAttachEvent(device_id);
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else if (event_type == DBT_DEVICEREMOVECOMPLETE)
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HandleDeviceDetachEvent(device_id);
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool PortableDeviceWatcherWin::GetMTPStorageInfoFromDeviceId(
5132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& storage_device_id,
5142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    string16* device_location,
5152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    string16* storage_object_id) const {
5162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
5172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(device_location);
5182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(storage_object_id);
5192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  MTPStorageMap::const_iterator storage_map_iter =
5202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      storage_map_.find(storage_device_id);
5212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (storage_map_iter == storage_map_.end())
5222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
5232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  MTPDeviceMap::const_iterator device_iter =
52590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      device_map_.find(storage_map_iter->second.location());
5262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (device_iter == device_map_.end())
5272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
5282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const StorageObjects& storage_objects = device_iter->second;
5292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (StorageObjects::const_iterator storage_object_iter =
5302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       storage_objects.begin(); storage_object_iter != storage_objects.end();
5312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       ++storage_object_iter) {
5322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (storage_device_id == storage_object_iter->object_persistent_id) {
53390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      *device_location = storage_map_iter->second.location();
5342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      *storage_object_id = storage_object_iter->object_temporary_id;
5352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return true;
5362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
5372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
5382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return false;
5392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
5422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)string16 PortableDeviceWatcherWin::GetStoragePathFromStorageId(
5432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& storage_unique_id) {
5442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Construct a dummy device path using the storage name. This is only used
5452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // for registering the device media file system.
5462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(!storage_unique_id.empty());
5472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return UTF8ToUTF16("\\\\" + storage_unique_id);
5482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void PortableDeviceWatcherWin::SetNotifications(
5512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    StorageMonitor::Receiver* notifications) {
5522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  storage_notifications_ = notifications;
5532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
555b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void PortableDeviceWatcherWin::EjectDevice(
556b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    const std::string& device_id,
557b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    base::Callback<void(StorageMonitor::EjectStatus)> callback) {
558b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  callback.Run(chrome::StorageMonitor::EJECT_FAILURE);
559b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
560b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PortableDeviceWatcherWin::EnumerateAttachedDevices() {
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(media_task_runner_.get());
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Devices* devices = new Devices;
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::PostTaskAndReplyWithResult(
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      media_task_runner_,
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FROM_HERE,
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&EnumerateAttachedDevicesOnBlockingThread, devices),
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices,
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 weak_ptr_factory_.GetWeakPtr(), base::Owned(devices)));
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PortableDeviceWatcherWin::OnDidEnumerateAttachedDevices(
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Devices* devices, const bool result) {
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(devices);
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!result)
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (Devices::const_iterator device_iter = devices->begin();
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       device_iter != devices->end(); ++device_iter) {
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    OnDidHandleDeviceAttachEvent(&(*device_iter), result);
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PortableDeviceWatcherWin::HandleDeviceAttachEvent(
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& pnp_device_id) {
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(media_task_runner_.get());
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DeviceDetails* device_details = new DeviceDetails;
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::PostTaskAndReplyWithResult(
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      media_task_runner_,
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FROM_HERE,
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&HandleDeviceAttachedEventOnBlockingThread, pnp_device_id,
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 device_details),
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent,
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 weak_ptr_factory_.GetWeakPtr(), base::Owned(device_details)));
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PortableDeviceWatcherWin::OnDidHandleDeviceAttachEvent(
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const DeviceDetails* device_details, const bool result) {
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(device_details);
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!result)
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const StorageObjects& storage_objects = device_details->storage_objects;
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const string16& name = device_details->name;
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const string16& location = device_details->location;
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!ContainsKey(device_map_, location));
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (StorageObjects::const_iterator storage_iter = storage_objects.begin();
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       storage_iter != storage_objects.end(); ++storage_iter) {
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& storage_id = storage_iter->object_persistent_id;
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(!ContainsKey(storage_map_, storage_id));
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Keep track of storage id and storage name to see how often we receive
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // empty values.
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    MediaStorageUtil::RecordDeviceInfoHistogram(false, storage_id, name);
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (storage_id.empty() || name.empty())
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Device can have several data partitions. Therefore, add the
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // partition identifier to the storage name. E.g.: "Nexus 7 (s10001)"
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    string16 storage_name(name + L" (" + storage_iter->object_temporary_id +
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        L')');
625c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    StorageInfo info(storage_id, storage_name, location,
626c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                     string16(), string16(), string16(), 0);
627c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    storage_map_[storage_id] = info;
6282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (storage_notifications_) {
62990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      info.set_location(GetStoragePathFromStorageId(storage_id));
630c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      storage_notifications_->ProcessAttach(info);
6312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  device_map_[location] = storage_objects;
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PortableDeviceWatcherWin::HandleDeviceDetachEvent(
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& pnp_device_id) {
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MTPDeviceMap::iterator device_iter = device_map_.find(pnp_device_id);
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (device_iter == device_map_.end())
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const StorageObjects& storage_objects = device_iter->second;
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (StorageObjects::const_iterator storage_object_iter =
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       storage_objects.begin(); storage_object_iter != storage_objects.end();
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++storage_object_iter) {
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string storage_id = storage_object_iter->object_persistent_id;
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    MTPStorageMap::iterator storage_map_iter = storage_map_.find(storage_id);
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(storage_map_iter != storage_map_.end());
65090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if (storage_notifications_) {
65190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      storage_notifications_->ProcessDetach(
65290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          storage_map_iter->second.device_id());
65390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    }
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    storage_map_.erase(storage_map_iter);
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  device_map_.erase(device_iter);
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace chrome
660