1a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 2a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// found in the LICENSE file. 4a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 55f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "extensions/browser/api/hid/hid_device_manager.h" 6a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 7a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include <limits> 8a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include <vector> 9a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 10a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/lazy_instance.h" 111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "content/public/browser/browser_thread.h" 121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "device/core/device_client.h" 131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "device/hid/hid_device_filter.h" 14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "device/hid/hid_service.h" 155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "extensions/browser/api/extensions_api_client.h" 161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "extensions/common/permissions/permissions_data.h" 171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "extensions/common/permissions/usb_device_permission.h" 18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciusing device::HidDeviceFilter; 20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)using device::HidService; 215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuusing device::HidUsageAndPage; 22a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace extensions { 24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 25a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)HidDeviceManager::HidDeviceManager(content::BrowserContext* context) 261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci : next_resource_id_(0) { 271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)HidDeviceManager::~HidDeviceManager() {} 30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// static 32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)BrowserContextKeyedAPIFactory<HidDeviceManager>* 33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)HidDeviceManager::GetFactoryInstance() { 34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) static base::LazyInstance<BrowserContextKeyedAPIFactory<HidDeviceManager> > 35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) factory = LAZY_INSTANCE_INITIALIZER; 36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return &factory.Get(); 37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)scoped_ptr<base::ListValue> HidDeviceManager::GetApiDevices( 401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const Extension* extension, 411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const std::vector<HidDeviceFilter>& filters) { 421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci DCHECK(IsCalledOnValidThread()); 43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) UpdateDevices(); 44a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci HidService* hid_service = device::DeviceClient::Get()->GetHidService(); 46a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) DCHECK(hid_service); 47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base::ListValue* api_devices = new base::ListValue(); 48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) for (ResourceIdToDeviceIdMap::const_iterator device_iter = 49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) device_ids_.begin(); 50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) device_iter != device_ids_.end(); 51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ++device_iter) { 52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) int resource_id = device_iter->first; 53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) device::HidDeviceId device_id = device_iter->second; 54a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) device::HidDeviceInfo device_info; 55116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 56a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (hid_service->GetDeviceInfo(device_id, &device_info)) { 571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!filters.empty() && 581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci !HidDeviceFilter::MatchesAny(device_info, filters)) { 591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci continue; 601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!HasPermission(extension, device_info)) { 631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci continue; 641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci core_api::hid::HidDeviceInfo api_device_info; 671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci api_device_info.device_id = resource_id; 681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci api_device_info.vendor_id = device_info.vendor_id; 691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci api_device_info.product_id = device_info.product_id; 701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci api_device_info.max_input_report_size = device_info.max_input_report_size; 711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci api_device_info.max_output_report_size = 721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci device_info.max_output_report_size; 731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci api_device_info.max_feature_report_size = 741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci device_info.max_feature_report_size; 751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for (std::vector<device::HidCollectionInfo>::const_iterator 771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci collections_iter = device_info.collections.begin(); 781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci collections_iter != device_info.collections.end(); 791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ++collections_iter) { 801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const device::HidCollectionInfo& collection = *collections_iter; 811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Don't expose sensitive data. 831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (collection.usage.IsProtected()) { 841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci continue; 855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 86116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci core_api::hid::HidCollectionInfo* api_collection = 881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci new core_api::hid::HidCollectionInfo(); 891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci api_collection->usage_page = collection.usage.usage_page; 901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci api_collection->usage = collection.usage.usage; 911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci api_collection->report_ids.resize(collection.report_ids.size()); 931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci std::copy(collection.report_ids.begin(), 941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci collection.report_ids.end(), 951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci api_collection->report_ids.begin()); 961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci api_device_info.collections.push_back(make_linked_ptr(api_collection)); 981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Expose devices with which user can communicate. 1011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (api_device_info.collections.size() > 0) { 1021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci api_devices->Append(api_device_info.ToValue().release()); 103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 106116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return scoped_ptr<base::ListValue>(api_devices); 108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool HidDeviceManager::GetDeviceInfo(int resource_id, 111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) device::HidDeviceInfo* device_info) { 1121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci DCHECK(IsCalledOnValidThread()); 113a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) UpdateDevices(); 1141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci HidService* hid_service = device::DeviceClient::Get()->GetHidService(); 115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) DCHECK(hid_service); 116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 117a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ResourceIdToDeviceIdMap::const_iterator device_iter = 118a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) device_ids_.find(resource_id); 119a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (device_iter == device_ids_.end()) 120a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return false; 121a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 122a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return hid_service->GetDeviceInfo(device_iter->second, device_info); 123a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 124a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 1251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool HidDeviceManager::HasPermission(const Extension* extension, 1261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const device::HidDeviceInfo& device_info) { 1271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci DCHECK(IsCalledOnValidThread()); 1281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci UsbDevicePermission::CheckParam usbParam( 1291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci device_info.vendor_id, 1301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci device_info.product_id, 1311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci UsbDevicePermissionData::UNSPECIFIED_INTERFACE); 1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (extension->permissions_data()->CheckAPIPermissionWithParam( 1331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci APIPermission::kUsbDevice, &usbParam)) { 1341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return true; 1351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (extension->permissions_data()->HasAPIPermission( 1381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci APIPermission::kU2fDevices)) { 1391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci HidDeviceFilter u2f_filter; 1401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci u2f_filter.SetUsagePage(0xF1D0); 1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (u2f_filter.Matches(device_info)) { 1421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return true; 1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return false; 1471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 1481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// static 1501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool HidDeviceManager::IsCalledOnValidThread() { 1511675a649fd7a8b3cb80ffddae2dc181f122353c5Ben Murdoch#if defined(OS_MACOSX) 1521675a649fd7a8b3cb80ffddae2dc181f122353c5Ben Murdoch // Migration from FILE thread to UI thread. OS X gets it first. 1531675a649fd7a8b3cb80ffddae2dc181f122353c5Ben Murdoch return content::BrowserThread::CurrentlyOn(content::BrowserThread::UI); 1541675a649fd7a8b3cb80ffddae2dc181f122353c5Ben Murdoch#else 1551675a649fd7a8b3cb80ffddae2dc181f122353c5Ben Murdoch // TODO(reillyg): Migrate Linux/CrOS and Windows as well. 1561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE); 1571675a649fd7a8b3cb80ffddae2dc181f122353c5Ben Murdoch#endif 1581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 1591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 160a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void HidDeviceManager::UpdateDevices() { 1611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci DCHECK(IsCalledOnValidThread()); 1621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci HidService* hid_service = device::DeviceClient::Get()->GetHidService(); 163a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) DCHECK(hid_service); 164a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 165a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) std::vector<device::HidDeviceInfo> devices; 166a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) hid_service->GetDevices(&devices); 167a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 168a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Build an updated bidi mapping between resource ID and underlying device ID. 169a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) DeviceIdToResourceIdMap new_resource_ids; 170a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ResourceIdToDeviceIdMap new_device_ids; 171a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) for (std::vector<device::HidDeviceInfo>::const_iterator iter = 172a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) devices.begin(); 173a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) iter != devices.end(); 174a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ++iter) { 175a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const device::HidDeviceInfo& device_info = *iter; 176a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) DeviceIdToResourceIdMap::iterator resource_iter = 177a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) resource_ids_.find(device_info.device_id); 178a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) int new_id; 179a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (resource_iter != resource_ids_.end()) { 180a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) new_id = resource_iter->second; 181a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } else { 182a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) DCHECK_LT(next_resource_id_, std::numeric_limits<int>::max()); 183a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) new_id = next_resource_id_++; 184a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 185a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) new_resource_ids[device_info.device_id] = new_id; 186a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) new_device_ids[new_id] = device_info.device_id; 187a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 188a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) device_ids_.swap(new_device_ids); 189a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) resource_ids_.swap(new_resource_ids); 190a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 191a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 192a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} // namespace extensions 193