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/bind.h" 11 #include "base/bind_helpers.h" 12 #include "base/logging.h" 13 #include "base/stl_util.h" 14 #include "chrome/browser/chrome_notification_types.h" 15 #include "chrome/browser/usb/usb_context.h" 16 #include "chrome/browser/usb/usb_device_handle.h" 17 #include "content/public/browser/browser_thread.h" 18 #include "content/public/browser/notification_observer.h" 19 #include "content/public/browser/notification_registrar.h" 20 #include "content/public/browser/notification_service.h" 21 #include "third_party/libusb/src/libusb/libusb.h" 22 23 #if defined(OS_CHROMEOS) 24 #include "base/chromeos/chromeos_version.h" 25 #include "chromeos/dbus/dbus_thread_manager.h" 26 #include "chromeos/dbus/permission_broker_client.h" 27 #endif // defined(OS_CHROMEOS) 28 29 namespace content { 30 31 class NotificationDetails; 32 class NotificationSource; 33 34 } // namespace content 35 36 using content::BrowserThread; 37 using std::vector; 38 39 namespace { 40 41 class ExitObserver : public content::NotificationObserver { 42 public: 43 explicit ExitObserver(UsbService* service) : service_(service) { 44 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, 45 content::NotificationService::AllSources()); 46 } 47 48 private: 49 // content::NotificationObserver 50 virtual void Observe(int type, 51 const content::NotificationSource& source, 52 const content::NotificationDetails& details) OVERRIDE { 53 if (type == chrome::NOTIFICATION_APP_TERMINATING) { 54 registrar_.RemoveAll(); 55 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, service_); 56 } 57 } 58 UsbService* service_; 59 content::NotificationRegistrar registrar_; 60 }; 61 62 } // namespace 63 64 using content::BrowserThread; 65 66 UsbService::UsbService() 67 : context_(new UsbContext()) { 68 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 69 } 70 71 UsbService::~UsbService() { 72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 73 for (DeviceMap::iterator it = devices_.begin(); 74 it != devices_.end(); ++it) { 75 it->second->OnDisconnect(); 76 } 77 } 78 79 UsbService* UsbService::GetInstance() { 80 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 81 // UsbService deletes itself upon APP_TERMINATING. 82 return Singleton<UsbService, LeakySingletonTraits<UsbService> >::get(); 83 } 84 85 void UsbService::FindDevices( 86 const uint16 vendor_id, 87 const uint16 product_id, 88 int interface_id, 89 const base::Callback<void(ScopedDeviceVector vector)>& callback) { 90 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 91 #if defined(OS_CHROMEOS) 92 // ChromeOS builds on non-ChromeOS machines (dev) should not attempt to 93 // use permission broker. 94 if (base::chromeos::IsRunningOnChromeOS()) { 95 chromeos::PermissionBrokerClient* client = 96 chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient(); 97 DCHECK(client) << "Could not get permission broker client."; 98 if (!client) { 99 callback.Run(ScopedDeviceVector()); 100 return; 101 } 102 103 BrowserThread::PostTask( 104 BrowserThread::UI, 105 FROM_HERE, 106 base::Bind(&chromeos::PermissionBrokerClient::RequestUsbAccess, 107 base::Unretained(client), 108 vendor_id, 109 product_id, 110 interface_id, 111 base::Bind(&UsbService::OnRequestUsbAccessReplied, 112 base::Unretained(this), 113 vendor_id, 114 product_id, 115 callback))); 116 } else { 117 FindDevicesImpl(vendor_id, product_id, callback, true); 118 } 119 #else 120 FindDevicesImpl(vendor_id, product_id, callback, true); 121 #endif // defined(OS_CHROMEOS) 122 } 123 124 void UsbService::GetDevices(std::vector<scoped_refptr<UsbDevice> >* devices) { 125 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 126 STLClearObject(devices); 127 RefreshDevices(); 128 129 for (DeviceMap::iterator it = devices_.begin(); 130 it != devices_.end(); ++it) { 131 devices->push_back(it->second); 132 } 133 } 134 135 void UsbService::OnRequestUsbAccessReplied( 136 const uint16 vendor_id, 137 const uint16 product_id, 138 const base::Callback<void(ScopedDeviceVector vectors)>& callback, 139 bool success) { 140 BrowserThread::PostTask( 141 BrowserThread::FILE, 142 FROM_HERE, 143 base::Bind(&UsbService::FindDevicesImpl, 144 base::Unretained(this), 145 vendor_id, 146 product_id, 147 callback, 148 success)); 149 } 150 151 void UsbService::FindDevicesImpl( 152 const uint16 vendor_id, 153 const uint16 product_id, 154 const base::Callback<void(ScopedDeviceVector vectors)>& callback, 155 bool success) { 156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 157 ScopedDeviceVector devices(new vector<scoped_refptr<UsbDevice> >()); 158 159 // If the permission broker was unable to obtain permission for the specified 160 // devices then there is no point in attempting to enumerate the devices. On 161 // platforms without a permission broker, we assume permission is granted. 162 if (!success) { 163 callback.Run(devices.Pass()); 164 return; 165 } 166 167 RefreshDevices(); 168 169 for (DeviceMap::iterator it = devices_.begin(); 170 it != devices_.end(); ++it) { 171 if (DeviceMatches(it->second, vendor_id, product_id)) 172 devices->push_back(it->second); 173 } 174 175 callback.Run(devices.Pass()); 176 } 177 178 void UsbService::RefreshDevices() { 179 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 180 181 libusb_device** platform_devices = NULL; 182 const ssize_t device_count = 183 libusb_get_device_list(context_->context(), &platform_devices); 184 185 std::set<UsbDevice*> connected_devices; 186 vector<PlatformUsbDevice> disconnected_devices; 187 188 // Populates new devices. 189 for (ssize_t i = 0; i < device_count; ++i) { 190 if (!ContainsKey(devices_, platform_devices[i])) { 191 libusb_device_descriptor descriptor; 192 // This test is needed. A valid vendor/produce pair is required. 193 if (0 != libusb_get_device_descriptor(platform_devices[i], &descriptor)) 194 continue; 195 UsbDevice* new_device = new UsbDevice(context_, 196 platform_devices[i], 197 descriptor.idVendor, 198 descriptor.idProduct); 199 devices_[platform_devices[i]] = new_device; 200 connected_devices.insert(new_device); 201 } else { 202 connected_devices.insert(devices_[platform_devices[i]].get()); 203 } 204 } 205 206 // Find disconnected devices. 207 for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { 208 if (!ContainsKey(connected_devices, it->second)) { 209 disconnected_devices.push_back(it->first); 210 } 211 } 212 213 // Remove disconnected devices from devices_. 214 for (size_t i = 0; i < disconnected_devices.size(); ++i) { 215 // UsbDevice will be destroyed after this. The corresponding 216 // PlatformUsbDevice will be unref'ed during this process. 217 devices_.erase(disconnected_devices[i]); 218 } 219 220 libusb_free_device_list(platform_devices, true); 221 } 222 223 bool UsbService::DeviceMatches(scoped_refptr<UsbDevice> device, 224 const uint16 vendor_id, 225 const uint16 product_id) { 226 return device->vendor_id() == vendor_id && device->product_id() == product_id; 227 } 228