| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/usb/usb_service.h" |
| |
| #include <set> |
| #include <vector> |
| |
| #include "base/basictypes.h" |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/logging.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/stl_util.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/usb/usb_context.h" |
| #include "chrome/browser/usb/usb_device.h" |
| #include "chrome/browser/usb/usb_device_handle.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/notification_observer.h" |
| #include "content/public/browser/notification_registrar.h" |
| #include "content/public/browser/notification_service.h" |
| #include "third_party/libusb/src/libusb/libusb.h" |
| |
| namespace content { |
| |
| class NotificationDetails; |
| class NotificationSource; |
| |
| } // namespace content |
| |
| using content::BrowserThread; |
| using std::vector; |
| |
| namespace { |
| |
| class ExitObserver : public content::NotificationObserver { |
| public: |
| explicit ExitObserver(UsbService* service) : service_(service) { |
| BrowserThread::PostTask( |
| BrowserThread::UI, FROM_HERE, |
| base::Bind(&content::NotificationRegistrar::Add, |
| base::Unretained(®istrar_), this, |
| chrome::NOTIFICATION_APP_TERMINATING, |
| content::NotificationService::AllSources())); |
| } |
| |
| private: |
| // content::NotificationObserver |
| virtual void Observe(int type, |
| const content::NotificationSource& source, |
| const content::NotificationDetails& details) OVERRIDE { |
| if (type == chrome::NOTIFICATION_APP_TERMINATING) { |
| BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, service_); |
| delete this; |
| } |
| } |
| UsbService* service_; |
| content::NotificationRegistrar registrar_; |
| }; |
| |
| } // namespace |
| |
| using content::BrowserThread; |
| |
| UsbService::UsbService(PlatformUsbContext context) |
| : context_(new UsbContext(context)), |
| next_unique_id_(0) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| // Will be deleted upon NOTIFICATION_APP_TERMINATING. |
| new ExitObserver(this); |
| } |
| |
| UsbService::~UsbService() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { |
| it->second->OnDisconnect(); |
| } |
| } |
| |
| struct InitUsbContextTraits : public LeakySingletonTraits<UsbService> { |
| // LeakySingletonTraits<UsbService> |
| static UsbService* New() { |
| PlatformUsbContext context = NULL; |
| if (libusb_init(&context) != LIBUSB_SUCCESS) |
| return NULL; |
| if (!context) |
| return NULL; |
| return new UsbService(context); |
| } |
| }; |
| |
| UsbService* UsbService::GetInstance() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| // UsbService deletes itself upon APP_TERMINATING. |
| return Singleton<UsbService, InitUsbContextTraits>::get(); |
| } |
| |
| void UsbService::GetDevices(std::vector<scoped_refptr<UsbDevice> >* devices) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| STLClearObject(devices); |
| RefreshDevices(); |
| |
| for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { |
| devices->push_back(it->second); |
| } |
| } |
| |
| scoped_refptr<UsbDevice> UsbService::GetDeviceById(uint32 unique_id) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| RefreshDevices(); |
| |
| for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { |
| if (it->second->unique_id() == unique_id) return it->second; |
| } |
| return NULL; |
| } |
| |
| void UsbService::RefreshDevices() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| |
| libusb_device** platform_devices = NULL; |
| const ssize_t device_count = |
| libusb_get_device_list(context_->context(), &platform_devices); |
| |
| std::set<UsbDevice*> connected_devices; |
| vector<PlatformUsbDevice> disconnected_devices; |
| |
| // Populates new devices. |
| for (ssize_t i = 0; i < device_count; ++i) { |
| if (!ContainsKey(devices_, platform_devices[i])) { |
| libusb_device_descriptor descriptor; |
| // This test is needed. A valid vendor/produce pair is required. |
| if (0 != libusb_get_device_descriptor(platform_devices[i], &descriptor)) |
| continue; |
| UsbDevice* new_device = new UsbDevice(context_, |
| platform_devices[i], |
| descriptor.idVendor, |
| descriptor.idProduct, |
| ++next_unique_id_); |
| devices_[platform_devices[i]] = new_device; |
| connected_devices.insert(new_device); |
| } else { |
| connected_devices.insert(devices_[platform_devices[i]].get()); |
| } |
| } |
| |
| // Find disconnected devices. |
| for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { |
| if (!ContainsKey(connected_devices, it->second)) { |
| disconnected_devices.push_back(it->first); |
| } |
| } |
| |
| // Remove disconnected devices from devices_. |
| for (size_t i = 0; i < disconnected_devices.size(); ++i) { |
| // UsbDevice will be destroyed after this. The corresponding |
| // PlatformUsbDevice will be unref'ed during this process. |
| devices_.erase(disconnected_devices[i]); |
| } |
| |
| libusb_free_device_list(platform_devices, true); |
| } |