Home | History | Annotate | Download | only in hid
      1 // Copyright 2014 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 "extensions/browser/api/hid/hid_device_manager.h"
      6 
      7 #include <limits>
      8 #include <vector>
      9 
     10 #include "base/lazy_instance.h"
     11 #include "content/public/browser/browser_thread.h"
     12 #include "device/core/device_client.h"
     13 #include "device/hid/hid_device_filter.h"
     14 #include "device/hid/hid_service.h"
     15 #include "extensions/browser/api/extensions_api_client.h"
     16 #include "extensions/common/permissions/permissions_data.h"
     17 #include "extensions/common/permissions/usb_device_permission.h"
     18 
     19 using device::HidDeviceFilter;
     20 using device::HidService;
     21 using device::HidUsageAndPage;
     22 
     23 namespace extensions {
     24 
     25 HidDeviceManager::HidDeviceManager(content::BrowserContext* context)
     26     : next_resource_id_(0) {
     27 }
     28 
     29 HidDeviceManager::~HidDeviceManager() {}
     30 
     31 // static
     32 BrowserContextKeyedAPIFactory<HidDeviceManager>*
     33 HidDeviceManager::GetFactoryInstance() {
     34   static base::LazyInstance<BrowserContextKeyedAPIFactory<HidDeviceManager> >
     35       factory = LAZY_INSTANCE_INITIALIZER;
     36   return &factory.Get();
     37 }
     38 
     39 scoped_ptr<base::ListValue> HidDeviceManager::GetApiDevices(
     40     const Extension* extension,
     41     const std::vector<HidDeviceFilter>& filters) {
     42   DCHECK(IsCalledOnValidThread());
     43   UpdateDevices();
     44 
     45   HidService* hid_service = device::DeviceClient::Get()->GetHidService();
     46   DCHECK(hid_service);
     47   base::ListValue* api_devices = new base::ListValue();
     48   for (ResourceIdToDeviceIdMap::const_iterator device_iter =
     49            device_ids_.begin();
     50        device_iter != device_ids_.end();
     51        ++device_iter) {
     52     int resource_id = device_iter->first;
     53     device::HidDeviceId device_id = device_iter->second;
     54     device::HidDeviceInfo device_info;
     55 
     56     if (hid_service->GetDeviceInfo(device_id, &device_info)) {
     57       if (!filters.empty() &&
     58           !HidDeviceFilter::MatchesAny(device_info, filters)) {
     59         continue;
     60       }
     61 
     62       if (!HasPermission(extension, device_info)) {
     63         continue;
     64       }
     65 
     66       core_api::hid::HidDeviceInfo api_device_info;
     67       api_device_info.device_id = resource_id;
     68       api_device_info.vendor_id = device_info.vendor_id;
     69       api_device_info.product_id = device_info.product_id;
     70       api_device_info.max_input_report_size = device_info.max_input_report_size;
     71       api_device_info.max_output_report_size =
     72           device_info.max_output_report_size;
     73       api_device_info.max_feature_report_size =
     74           device_info.max_feature_report_size;
     75 
     76       for (std::vector<device::HidCollectionInfo>::const_iterator
     77                collections_iter = device_info.collections.begin();
     78            collections_iter != device_info.collections.end();
     79            ++collections_iter) {
     80         const device::HidCollectionInfo& collection = *collections_iter;
     81 
     82         // Don't expose sensitive data.
     83         if (collection.usage.IsProtected()) {
     84           continue;
     85         }
     86 
     87         core_api::hid::HidCollectionInfo* api_collection =
     88             new core_api::hid::HidCollectionInfo();
     89         api_collection->usage_page = collection.usage.usage_page;
     90         api_collection->usage = collection.usage.usage;
     91 
     92         api_collection->report_ids.resize(collection.report_ids.size());
     93         std::copy(collection.report_ids.begin(),
     94                   collection.report_ids.end(),
     95                   api_collection->report_ids.begin());
     96 
     97         api_device_info.collections.push_back(make_linked_ptr(api_collection));
     98       }
     99 
    100       // Expose devices with which user can communicate.
    101       if (api_device_info.collections.size() > 0) {
    102         api_devices->Append(api_device_info.ToValue().release());
    103       }
    104     }
    105   }
    106 
    107   return scoped_ptr<base::ListValue>(api_devices);
    108 }
    109 
    110 bool HidDeviceManager::GetDeviceInfo(int resource_id,
    111                                      device::HidDeviceInfo* device_info) {
    112   DCHECK(IsCalledOnValidThread());
    113   UpdateDevices();
    114   HidService* hid_service = device::DeviceClient::Get()->GetHidService();
    115   DCHECK(hid_service);
    116 
    117   ResourceIdToDeviceIdMap::const_iterator device_iter =
    118       device_ids_.find(resource_id);
    119   if (device_iter == device_ids_.end())
    120     return false;
    121 
    122   return hid_service->GetDeviceInfo(device_iter->second, device_info);
    123 }
    124 
    125 bool HidDeviceManager::HasPermission(const Extension* extension,
    126                                      const device::HidDeviceInfo& device_info) {
    127   DCHECK(IsCalledOnValidThread());
    128   UsbDevicePermission::CheckParam usbParam(
    129       device_info.vendor_id,
    130       device_info.product_id,
    131       UsbDevicePermissionData::UNSPECIFIED_INTERFACE);
    132   if (extension->permissions_data()->CheckAPIPermissionWithParam(
    133           APIPermission::kUsbDevice, &usbParam)) {
    134     return true;
    135   }
    136 
    137   if (extension->permissions_data()->HasAPIPermission(
    138           APIPermission::kU2fDevices)) {
    139     HidDeviceFilter u2f_filter;
    140     u2f_filter.SetUsagePage(0xF1D0);
    141     if (u2f_filter.Matches(device_info)) {
    142       return true;
    143     }
    144   }
    145 
    146   return false;
    147 }
    148 
    149 // static
    150 bool HidDeviceManager::IsCalledOnValidThread() {
    151 #if defined(OS_MACOSX)
    152   // Migration from FILE thread to UI thread. OS X gets it first.
    153   return content::BrowserThread::CurrentlyOn(content::BrowserThread::UI);
    154 #else
    155   // TODO(reillyg): Migrate Linux/CrOS and Windows as well.
    156   return content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE);
    157 #endif
    158 }
    159 
    160 void HidDeviceManager::UpdateDevices() {
    161   DCHECK(IsCalledOnValidThread());
    162   HidService* hid_service = device::DeviceClient::Get()->GetHidService();
    163   DCHECK(hid_service);
    164 
    165   std::vector<device::HidDeviceInfo> devices;
    166   hid_service->GetDevices(&devices);
    167 
    168   // Build an updated bidi mapping between resource ID and underlying device ID.
    169   DeviceIdToResourceIdMap new_resource_ids;
    170   ResourceIdToDeviceIdMap new_device_ids;
    171   for (std::vector<device::HidDeviceInfo>::const_iterator iter =
    172            devices.begin();
    173        iter != devices.end();
    174        ++iter) {
    175     const device::HidDeviceInfo& device_info = *iter;
    176     DeviceIdToResourceIdMap::iterator resource_iter =
    177         resource_ids_.find(device_info.device_id);
    178     int new_id;
    179     if (resource_iter != resource_ids_.end()) {
    180       new_id = resource_iter->second;
    181     } else {
    182       DCHECK_LT(next_resource_id_, std::numeric_limits<int>::max());
    183       new_id = next_resource_id_++;
    184     }
    185     new_resource_ids[device_info.device_id] = new_id;
    186     new_device_ids[new_id] = device_info.device_id;
    187   }
    188   device_ids_.swap(new_device_ids);
    189   resource_ids_.swap(new_resource_ids);
    190 }
    191 
    192 }  // namespace extensions
    193