| Index: device/usb/usb_service_impl.cc | 
| diff --git a/device/usb/usb_service_impl.cc b/device/usb/usb_service_impl.cc | 
| index 215aba1817af302542804f3c510ecc01aa08706e..a23a4a1548b913efe763a5b140951f056b6d424c 100644 | 
| --- a/device/usb/usb_service_impl.cc | 
| +++ b/device/usb/usb_service_impl.cc | 
| @@ -7,10 +7,12 @@ | 
| #include <map> | 
| #include <set> | 
|  | 
| +#include "base/bind.h" | 
| #include "base/lazy_instance.h" | 
| #include "base/message_loop/message_loop.h" | 
| #include "base/single_thread_task_runner.h" | 
| #include "base/stl_util.h" | 
| +#include "base/thread_task_runner_handle.h" | 
| #include "device/usb/usb_context.h" | 
| #include "device/usb/usb_device_impl.h" | 
| #include "device/usb/usb_error.h" | 
| @@ -47,13 +49,31 @@ class UsbServiceImpl : public UsbService, | 
| // Enumerate USB devices from OS and update devices_ map. | 
| void RefreshDevices(); | 
|  | 
| +  // Adds a new UsbDevice to the devices_ map based on the given libusb device. | 
| +  scoped_refptr<UsbDeviceImpl> AddDevice(PlatformUsbDevice platform_device); | 
| + | 
| +  // Handle hotplug events from libusb. | 
| +  static int LIBUSB_CALL HotplugCallback(libusb_context* context, | 
| +                                         PlatformUsbDevice device, | 
| +                                         libusb_hotplug_event event, | 
| +                                         void* user_data); | 
| +  // These functions release a reference to the provided platform device. | 
| +  void OnDeviceAdded(PlatformUsbDevice platform_device); | 
| +  void OnDeviceRemoved(PlatformUsbDevice platform_device); | 
| + | 
| scoped_refptr<UsbContext> context_; | 
| scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | 
| scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; | 
|  | 
| -  // TODO(reillyg): Figure out a better solution. | 
| +  // TODO(reillyg): Figure out a better solution for device IDs. | 
| uint32 next_unique_id_; | 
|  | 
| +  // When available the device list will be updated when new devices are | 
| +  // connected instead of only when a full enumeration is requested. | 
| +  // TODO(reillyg): Support this on all platforms. crbug.com/411715 | 
| +  bool hotplug_enabled_; | 
| +  libusb_hotplug_callback_handle hotplug_handle_; | 
| + | 
| // The map from unique IDs to UsbDevices. | 
| typedef std::map<uint32, scoped_refptr<UsbDeviceImpl> > DeviceMap; | 
| DeviceMap devices_; | 
| @@ -80,10 +100,13 @@ void UsbServiceImpl::GetDevices( | 
| std::vector<scoped_refptr<UsbDevice> >* devices) { | 
| DCHECK(CalledOnValidThread()); | 
| STLClearObject(devices); | 
| -  RefreshDevices(); | 
|  | 
| -  for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { | 
| -    devices->push_back(it->second); | 
| +  if (!hotplug_enabled_) { | 
| +    RefreshDevices(); | 
| +  } | 
| + | 
| +  for (const auto& map_entry : devices_) { | 
| +    devices->push_back(map_entry.second); | 
| } | 
| } | 
|  | 
| @@ -97,14 +120,29 @@ UsbServiceImpl::UsbServiceImpl( | 
| scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) | 
| : context_(new UsbContext(context)), | 
| ui_task_runner_(ui_task_runner), | 
| -      next_unique_id_(0) { | 
| +      next_unique_id_(0), | 
| +      hotplug_enabled_(false) { | 
| base::MessageLoop::current()->AddDestructionObserver(this); | 
| +  task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 
| +  int rv = libusb_hotplug_register_callback( | 
| +      context_->context(), | 
| +      static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | | 
| +                                        LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), | 
| +      LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, | 
| +      LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, | 
| +      &UsbServiceImpl::HotplugCallback, this, &hotplug_handle_); | 
| +  if (rv == LIBUSB_SUCCESS) { | 
| +    hotplug_enabled_ = true; | 
| +  } | 
| } | 
|  | 
| UsbServiceImpl::~UsbServiceImpl() { | 
| base::MessageLoop::current()->RemoveDestructionObserver(this); | 
| -  for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { | 
| -    it->second->OnDisconnect(); | 
| +  if (hotplug_enabled_) { | 
| +    libusb_hotplug_deregister_callback(context_->context(), hotplug_handle_); | 
| +  } | 
| +  for (const auto& map_entry : devices_) { | 
| +    map_entry.second->OnDisconnect(); | 
| } | 
| } | 
|  | 
| @@ -125,31 +163,10 @@ void UsbServiceImpl::RefreshDevices() { | 
| // Populates new devices. | 
| for (ssize_t i = 0; i < device_count; ++i) { | 
| if (!ContainsKey(platform_devices_, platform_devices[i])) { | 
| -      libusb_device_descriptor descriptor; | 
| -      const int rv = | 
| -          libusb_get_device_descriptor(platform_devices[i], &descriptor); | 
| -      // This test is needed. A valid vendor/produce pair is required. | 
| -      if (rv != LIBUSB_SUCCESS) { | 
| -        VLOG(1) << "Failed to get device descriptor: " | 
| -                << ConvertPlatformUsbErrorToString(rv); | 
| -        continue; | 
| +      scoped_refptr<UsbDeviceImpl> new_device = AddDevice(platform_devices[i]); | 
| +      if (new_device) { | 
| +        connected_devices.insert(new_device.get()); | 
| } | 
| - | 
| -      uint32 unique_id; | 
| -      do { | 
| -        unique_id = ++next_unique_id_; | 
| -      } while (devices_.find(unique_id) != devices_.end()); | 
| - | 
| -      scoped_refptr<UsbDeviceImpl> new_device( | 
| -          new UsbDeviceImpl(context_, | 
| -                            ui_task_runner_, | 
| -                            platform_devices[i], | 
| -                            descriptor.idVendor, | 
| -                            descriptor.idProduct, | 
| -                            unique_id)); | 
| -      platform_devices_[platform_devices[i]] = new_device; | 
| -      devices_[unique_id] = new_device; | 
| -      connected_devices.insert(new_device.get()); | 
| } else { | 
| connected_devices.insert(platform_devices_[platform_devices[i]].get()); | 
| } | 
| @@ -176,6 +193,96 @@ void UsbServiceImpl::RefreshDevices() { | 
| libusb_free_device_list(platform_devices, true); | 
| } | 
|  | 
| +scoped_refptr<UsbDeviceImpl> UsbServiceImpl::AddDevice( | 
| +    PlatformUsbDevice platform_device) { | 
| +  libusb_device_descriptor descriptor; | 
| +  int rv = libusb_get_device_descriptor(platform_device, &descriptor); | 
| +  if (rv == LIBUSB_SUCCESS) { | 
| +    uint32 unique_id; | 
| +    do { | 
| +      unique_id = ++next_unique_id_; | 
| +    } while (devices_.find(unique_id) != devices_.end()); | 
| + | 
| +    scoped_refptr<UsbDeviceImpl> new_device(new UsbDeviceImpl( | 
| +        context_, ui_task_runner_, platform_device, descriptor.idVendor, | 
| +        descriptor.idProduct, unique_id)); | 
| +    platform_devices_[platform_device] = new_device; | 
| +    devices_[unique_id] = new_device; | 
| +    return new_device; | 
| +  } else { | 
| +    VLOG(1) << "Failed to get device descriptor: " | 
| +            << ConvertPlatformUsbErrorToString(rv); | 
| +    return nullptr; | 
| +  } | 
| +} | 
| + | 
| +// static | 
| +int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context, | 
| +                                                PlatformUsbDevice device, | 
| +                                                libusb_hotplug_event event, | 
| +                                                void* user_data) { | 
| +  // It is safe to access the UsbServiceImpl* here because libusb takes a lock | 
| +  // around registering, deregistering and calling hotplug callback functions | 
| +  // and so guarantees that this function will not be called by the event | 
| +  // processing thread after it has been deregistered. | 
| +  UsbServiceImpl* self = reinterpret_cast<UsbServiceImpl*>(user_data); | 
| +  switch (event) { | 
| +    case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: | 
| +      libusb_ref_device(device);  // Released in OnDeviceAdded. | 
| +      if (self->task_runner_->BelongsToCurrentThread()) { | 
| +        self->OnDeviceAdded(device); | 
| +      } else { | 
| +        self->task_runner_->PostTask( | 
| +            FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceAdded, | 
| +                                  base::Unretained(self), device)); | 
| +      } | 
| +      break; | 
| +    case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: | 
| +      libusb_ref_device(device);  // Released in OnDeviceRemoved. | 
| +      if (self->task_runner_->BelongsToCurrentThread()) { | 
| +        self->OnDeviceRemoved(device); | 
| +      } else { | 
| +        self->task_runner_->PostTask( | 
| +            FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceRemoved, | 
| +                                  base::Unretained(self), device)); | 
| +      } | 
| +      break; | 
| +    default: | 
| +      NOTREACHED(); | 
| +  } | 
| + | 
| +  return 0; | 
| +} | 
| + | 
| +void UsbServiceImpl::OnDeviceAdded(PlatformUsbDevice platform_device) { | 
| +  DCHECK(CalledOnValidThread()); | 
| +  DCHECK(!ContainsKey(platform_devices_, platform_device)); | 
| + | 
| +  AddDevice(platform_device); | 
| +  libusb_unref_device(platform_device); | 
| +} | 
| + | 
| +void UsbServiceImpl::OnDeviceRemoved(PlatformUsbDevice platform_device) { | 
| +  DCHECK(CalledOnValidThread()); | 
| + | 
| +  PlatformDeviceMap::iterator it = platform_devices_.find(platform_device); | 
| +  if (it != platform_devices_.end()) { | 
| +    scoped_refptr<UsbDeviceImpl> device = it->second; | 
| +    DeviceMap::iterator dev_it = devices_.find(device->unique_id()); | 
| +    if (dev_it != devices_.end()) { | 
| +      devices_.erase(dev_it); | 
| +    } else { | 
| +      NOTREACHED(); | 
| +    } | 
| +    device->OnDisconnect(); | 
| +    platform_devices_.erase(it); | 
| +  } else { | 
| +    NOTREACHED(); | 
| +  } | 
| + | 
| +  libusb_unref_device(platform_device); | 
| +} | 
| + | 
| // static | 
| UsbService* UsbService::GetInstance( | 
| scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { | 
|  |