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(®istrar_), 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 } 165