- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / usb / usb_service.cc
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:
40   explicit ExitObserver(UsbService* service) : service_(service) {
41     BrowserThread::PostTask(
42         BrowserThread::UI, FROM_HERE,
43         base::Bind(&content::NotificationRegistrar::Add,
44                    base::Unretained(&registrar_), this,
45                    chrome::NOTIFICATION_APP_TERMINATING,
46                    content::NotificationService::AllSources()));
47   }
48
49  private:
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_);
56       delete this;
57     }
58   }
59   UsbService* service_;
60   content::NotificationRegistrar registrar_;
61 };
62
63 }  // namespace
64
65 using content::BrowserThread;
66
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
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>
84   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
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
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
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
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 }