Home | History | Annotate | Download | only in usb
      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