1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/usb/usb_service.h"
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/logging.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/stl_util.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/usb/usb_context.h"
18 #include "chrome/browser/usb/usb_device.h"
19 #include "chrome/browser/usb/usb_device_handle.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/notification_observer.h"
22 #include "content/public/browser/notification_registrar.h"
23 #include "content/public/browser/notification_service.h"
24 #include "third_party/libusb/src/libusb/libusb.h"
28 class NotificationDetails;
29 class NotificationSource;
31 } // namespace content
33 using content::BrowserThread;
38 class ExitObserver : public content::NotificationObserver {
40 explicit ExitObserver(UsbService* service) : service_(service) {
41 BrowserThread::PostTask(
42 BrowserThread::UI, FROM_HERE,
43 base::Bind(&content::NotificationRegistrar::Add,
44 base::Unretained(®istrar_), this,
45 chrome::NOTIFICATION_APP_TERMINATING,
46 content::NotificationService::AllSources()));
50 // content::NotificationObserver
51 virtual void Observe(int type,
52 const content::NotificationSource& source,
53 const content::NotificationDetails& details) OVERRIDE {
54 if (type == chrome::NOTIFICATION_APP_TERMINATING) {
55 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, service_);
60 content::NotificationRegistrar registrar_;
65 using content::BrowserThread;
67 UsbService::UsbService(PlatformUsbContext context)
68 : context_(new UsbContext(context)),
70 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
71 // Will be deleted upon NOTIFICATION_APP_TERMINATING.
72 new ExitObserver(this);
75 UsbService::~UsbService() {
76 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
77 for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
78 it->second->OnDisconnect();
82 struct InitUsbContextTraits : public LeakySingletonTraits<UsbService> {
83 // LeakySingletonTraits<UsbService>
84 static UsbService* New() {
85 PlatformUsbContext context = NULL;
86 if (libusb_init(&context) != LIBUSB_SUCCESS)
90 return new UsbService(context);
94 UsbService* UsbService::GetInstance() {
95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
96 // UsbService deletes itself upon APP_TERMINATING.
97 return Singleton<UsbService, InitUsbContextTraits>::get();
100 void UsbService::GetDevices(std::vector<scoped_refptr<UsbDevice> >* devices) {
101 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
102 STLClearObject(devices);
105 for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
106 devices->push_back(it->second);
110 scoped_refptr<UsbDevice> UsbService::GetDeviceById(uint32 unique_id) {
111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
114 for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
115 if (it->second->unique_id() == unique_id) return it->second;
120 void UsbService::RefreshDevices() {
121 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
123 libusb_device** platform_devices = NULL;
124 const ssize_t device_count =
125 libusb_get_device_list(context_->context(), &platform_devices);
127 std::set<UsbDevice*> connected_devices;
128 vector<PlatformUsbDevice> disconnected_devices;
130 // Populates new devices.
131 for (ssize_t i = 0; i < device_count; ++i) {
132 if (!ContainsKey(devices_, platform_devices[i])) {
133 libusb_device_descriptor descriptor;
134 // This test is needed. A valid vendor/produce pair is required.
135 if (0 != libusb_get_device_descriptor(platform_devices[i], &descriptor))
137 UsbDevice* new_device = new UsbDevice(context_,
140 descriptor.idProduct,
142 devices_[platform_devices[i]] = new_device;
143 connected_devices.insert(new_device);
145 connected_devices.insert(devices_[platform_devices[i]].get());
149 // Find disconnected devices.
150 for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
151 if (!ContainsKey(connected_devices, it->second)) {
152 disconnected_devices.push_back(it->first);
156 // Remove disconnected devices from devices_.
157 for (size_t i = 0; i < disconnected_devices.size(); ++i) {
158 // UsbDevice will be destroyed after this. The corresponding
159 // PlatformUsbDevice will be unref'ed during this process.
160 devices_.erase(disconnected_devices[i]);
163 libusb_free_device_list(platform_devices, true);