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.
4
5 #include "chrome/browser/usb/usb_service.h"
6
7 #include <set>
8 #include <vector>
9
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"
25
26 namespace content {
27
28 class NotificationDetails;
29 class NotificationSource;
30
31 } // namespace content
32
33 using content::BrowserThread;
34 using std::vector;
35
36 namespace {
37
38 class ExitObserver : public content::NotificationObserver {
39 public:
ExitObserver(UsbService * service)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()));
47 }
48
49 private:
50 // content::NotificationObserver
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)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_);
56 delete this;
57 }
58 }
59 UsbService* service_;
60 content::NotificationRegistrar registrar_;
61 };
62
63 } // namespace
64
65 using content::BrowserThread;
66
UsbService(PlatformUsbContext context)67 UsbService::UsbService(PlatformUsbContext context)
68 : context_(new UsbContext(context)),
69 next_unique_id_(0) {
70 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
71 // Will be deleted upon NOTIFICATION_APP_TERMINATING.
72 new ExitObserver(this);
73 }
74
~UsbService()75 UsbService::~UsbService() {
76 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
77 for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
78 it->second->OnDisconnect();
79 }
80 }
81
82 struct InitUsbContextTraits : public LeakySingletonTraits<UsbService> {
83 // LeakySingletonTraits<UsbService>
NewInitUsbContextTraits84 static UsbService* New() {
85 PlatformUsbContext context = NULL;
86 if (libusb_init(&context) != LIBUSB_SUCCESS)
87 return NULL;
88 if (!context)
89 return NULL;
90 return new UsbService(context);
91 }
92 };
93
GetInstance()94 UsbService* UsbService::GetInstance() {
95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
96 // UsbService deletes itself upon APP_TERMINATING.
97 return Singleton<UsbService, InitUsbContextTraits>::get();
98 }
99
GetDevices(std::vector<scoped_refptr<UsbDevice>> * devices)100 void UsbService::GetDevices(std::vector<scoped_refptr<UsbDevice> >* devices) {
101 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
102 STLClearObject(devices);
103 RefreshDevices();
104
105 for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
106 devices->push_back(it->second);
107 }
108 }
109
GetDeviceById(uint32 unique_id)110 scoped_refptr<UsbDevice> UsbService::GetDeviceById(uint32 unique_id) {
111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
112 RefreshDevices();
113
114 for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
115 if (it->second->unique_id() == unique_id) return it->second;
116 }
117 return NULL;
118 }
119
RefreshDevices()120 void UsbService::RefreshDevices() {
121 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
122
123 libusb_device** platform_devices = NULL;
124 const ssize_t device_count =
125 libusb_get_device_list(context_->context(), &platform_devices);
126
127 std::set<UsbDevice*> connected_devices;
128 vector<PlatformUsbDevice> disconnected_devices;
129
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))
136 continue;
137 UsbDevice* new_device = new UsbDevice(context_,
138 platform_devices[i],
139 descriptor.idVendor,
140 descriptor.idProduct,
141 ++next_unique_id_);
142 devices_[platform_devices[i]] = new_device;
143 connected_devices.insert(new_device);
144 } else {
145 connected_devices.insert(devices_[platform_devices[i]].get());
146 }
147 }
148
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);
153 }
154 }
155
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]);
161 }
162
163 libusb_free_device_list(platform_devices, true);
164 }
165