diff options
author | rockot <rockot@chromium.org> | 2015-05-27 17:42:53 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-05-28 00:43:26 +0000 |
commit | 249cc25c5e0a2e338cd3efec6e1a3964e7c983e9 (patch) | |
tree | e156ae25c3b6c092cd6075565c44230bb97a2091 | |
parent | d6e6531d7c1449e06d8768f11b8a8d2f357dc220 (diff) | |
download | chromium_src-249cc25c5e0a2e338cd3efec6e1a3964e7c983e9.zip chromium_src-249cc25c5e0a2e338cd3efec6e1a3964e7c983e9.tar.gz chromium_src-249cc25c5e0a2e338cd3efec6e1a3964e7c983e9.tar.bz2 |
Build a basic Mojo service framework for device/usb
BUG=492805
R=reillyg@chromium.org
Review URL: https://codereview.chromium.org/1155163008
Cr-Commit-Position: refs/heads/master@{#331707}
26 files changed, 970 insertions, 4 deletions
diff --git a/chrome/browser/OWNERS b/chrome/browser/OWNERS index d0e2a91..d8186f5 100644 --- a/chrome/browser/OWNERS +++ b/chrome/browser/OWNERS @@ -10,6 +10,9 @@ per-file chrome_content_browser_client_unittest.cc=* per-file chrome_browser_field_trials*=asvitkine@chromium.org per-file chrome_browser_field_trials*=stevet@chromium.org +per-file chrome_device_client*=reillyg@chromium.org +per-file chrome_device_client*=rockot@chromium.org + per-file chrome_elf_init_*=csharp@chromium.org per-file chrome_elf_init_*=robertshield@chromium.org diff --git a/chrome/browser/chrome_device_client.cc b/chrome/browser/chrome_device_client.cc index 835a4c3..d0a31a6 100644 --- a/chrome/browser/chrome_device_client.cc +++ b/chrome/browser/chrome_device_client.cc @@ -5,12 +5,34 @@ #include "chrome/browser/chrome_device_client.h" #include "base/logging.h" +#include "base/macros.h" #include "content/public/browser/browser_thread.h" #include "device/hid/hid_service.h" +#include "device/usb/public/cpp/device_manager_delegate.h" +#include "device/usb/public/cpp/device_manager_factory.h" +#include "device/usb/public/interfaces/device.mojom.h" #include "device/usb/usb_service.h" using content::BrowserThread; +namespace { + +// DeviceManagerDelegate implementation which allows access to all devices. +class BrowserDeviceManagerDelegate : public device::usb::DeviceManagerDelegate { + public: + BrowserDeviceManagerDelegate() {} + ~BrowserDeviceManagerDelegate() override {} + + private: + bool IsDeviceAllowed(const device::usb::DeviceInfo& device) override { + return true; + } + + DISALLOW_COPY_AND_ASSIGN(BrowserDeviceManagerDelegate); +}; + +} // namespace + ChromeDeviceClient::ChromeDeviceClient() {} ChromeDeviceClient::~ChromeDeviceClient() {} @@ -21,6 +43,14 @@ device::UsbService* ChromeDeviceClient::GetUsbService() { BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); } +void ChromeDeviceClient::ConnectToUSBDeviceManager( + mojo::InterfaceRequest<device::usb::DeviceManager> request) { + device::usb::DeviceManagerFactory::Build( + request.Pass(), + scoped_ptr<device::usb::DeviceManagerDelegate>( + new BrowserDeviceManagerDelegate)); +} + device::HidService* ChromeDeviceClient::GetHidService() { DCHECK_CURRENTLY_ON(BrowserThread::UI); return device::HidService::GetInstance( diff --git a/chrome/browser/chrome_device_client.h b/chrome/browser/chrome_device_client.h index a09c36d..1d1eb38 100644 --- a/chrome/browser/chrome_device_client.h +++ b/chrome/browser/chrome_device_client.h @@ -15,10 +15,12 @@ class ChromeDeviceClient : device::DeviceClient { public: ChromeDeviceClient(); - virtual ~ChromeDeviceClient(); + ~ChromeDeviceClient() override; // device::DeviceClient implementation device::UsbService* GetUsbService() override; + void ConnectToUSBDeviceManager( + mojo::InterfaceRequest<device::usb::DeviceManager> request) override; device::HidService* GetHidService() override; private: diff --git a/device/BUILD.gn b/device/BUILD.gn index d80e50d..ee53932 100644 --- a/device/BUILD.gn +++ b/device/BUILD.gn @@ -91,6 +91,8 @@ test("device_unittests") { if (!is_android && !is_ios) { sources += [ "test/usb_test_gadget_impl.cc", + "usb/device_impl_unittest.cc", + "usb/device_manager_impl_unittest.cc", "usb/usb_context_unittest.cc", "usb/usb_device_filter_unittest.cc", "usb/usb_device_handle_unittest.cc", @@ -98,6 +100,9 @@ test("device_unittests") { "usb/usb_service_unittest.cc", ] deps += [ + "//device/core", + "//device/usb/public/cpp", + "//device/usb/public/interfaces", "//device/usb", "//device/usb:mocks", "//third_party/libusb", diff --git a/device/core/BUILD.gn b/device/core/BUILD.gn index 2f25eef..6b84cea 100644 --- a/device/core/BUILD.gn +++ b/device/core/BUILD.gn @@ -10,7 +10,8 @@ source_set("core") { "device_monitor_win.h", ] - deps = [ + public_deps = [ "//base", + "//third_party/mojo/src/mojo/public/cpp/bindings", ] } diff --git a/device/core/core.gyp b/device/core/core.gyp index 3137de5..034b9e3 100644 --- a/device/core/core.gyp +++ b/device/core/core.gyp @@ -19,6 +19,9 @@ 'device_monitor_win.cc', 'device_monitor_win.h', ], + 'dependencies': [ + '<(DEPTH)/third_party/mojo/mojo_public.gyp:mojo_cpp_bindings', + ], }, ], } diff --git a/device/core/device_client.cc b/device/core/device_client.cc index 5202122..2aca216 100644 --- a/device/core/device_client.cc +++ b/device/core/device_client.cc @@ -35,6 +35,10 @@ UsbService* DeviceClient::GetUsbService() { return NULL; } +void DeviceClient::ConnectToUSBDeviceManager( + mojo::InterfaceRequest<usb::DeviceManager> request) { +} + HidService* DeviceClient::GetHidService() { // This should never be called by clients which do not support the HID API. NOTREACHED(); diff --git a/device/core/device_client.h b/device/core/device_client.h index b4a9934..00fa179 100644 --- a/device/core/device_client.h +++ b/device/core/device_client.h @@ -6,12 +6,17 @@ #define DEVICE_CORE_DEVICE_CLIENT_H_ #include "base/macros.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" namespace device { class HidService; class UsbService; +namespace usb { +class DeviceManager; +} + // Interface used by consumers of //device APIs to get pointers to the service // singletons appropriate for a given embedding application. For an example see // //chrome/browser/chrome_device_client.h. @@ -21,7 +26,7 @@ class DeviceClient { DeviceClient(); // Destruction clears the single instance. - ~DeviceClient(); + virtual ~DeviceClient(); // Returns the single instance of |this|. static DeviceClient* Get(); @@ -29,6 +34,11 @@ class DeviceClient { // Returns the UsbService instance for this embedder. virtual UsbService* GetUsbService(); + // Connects a USB DeviceManager client to a concrete implementation. If + // no such implementation is available the request is dropped. + virtual void ConnectToUSBDeviceManager( + mojo::InterfaceRequest<usb::DeviceManager> request); + // Returns the HidService instance for this embedder. virtual HidService* GetHidService(); diff --git a/device/device_tests.gyp b/device/device_tests.gyp index 6c48886..7604cab 100644 --- a/device/device_tests.gyp +++ b/device/device_tests.gyp @@ -66,6 +66,8 @@ 'serial/serial_service_unittest.cc', 'test/run_all_unittests.cc', 'test/usb_test_gadget_impl.cc', + 'usb/device_impl_unittest.cc', + 'usb/device_manager_impl_unittest.cc', 'usb/usb_context_unittest.cc', 'usb/usb_device_filter_unittest.cc', 'usb/usb_device_handle_unittest.cc', diff --git a/device/usb/BUILD.gn b/device/usb/BUILD.gn index aa74a106..a577538 100644 --- a/device/usb/BUILD.gn +++ b/device/usb/BUILD.gn @@ -9,6 +9,12 @@ generated_ids = "$target_gen_dir/usb_ids_gen.cc" source_set("usb") { sources = [ + "device_impl.cc", + "device_impl.h", + "device_manager_impl.cc", + "device_manager_impl.h", + "type_converters.cc", + "type_converters.h", "usb_context.cc", "usb_context.h", "usb_descriptors.cc", @@ -39,6 +45,8 @@ source_set("usb") { "//base/third_party/dynamic_annotations", "//components/device_event_log", "//device/core", + "//device/usb/public/cpp", + "//device/usb/public/interfaces", "//net", "//third_party/libusb", ] diff --git a/device/usb/device_impl.cc b/device/usb/device_impl.cc new file mode 100644 index 0000000..2011bdc --- /dev/null +++ b/device/usb/device_impl.cc @@ -0,0 +1,34 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/usb/device_impl.h" +#include "device/usb/type_converters.h" +#include "device/usb/usb_device.h" + +namespace device { +namespace usb { + +DeviceImpl::DeviceImpl(scoped_refptr<UsbDevice> device, + mojo::InterfaceRequest<Device> request) + : binding_(this, request.Pass()), + device_(device), + info_(DeviceInfo::From(*device)) { + // TODO(rockot/reillyg): Support more than just the current configuration. + // Also, converting configuration info should be done in the TypeConverter, + // but GetConfiguration() is non-const so we do it here for now. + const UsbConfigDescriptor* config = device->GetConfiguration(); + info_->configurations = mojo::Array<ConfigurationInfoPtr>::New(0); + if (config) + info_->configurations.push_back(ConfigurationInfo::From(*config)); +} + +DeviceImpl::~DeviceImpl() { +} + +void DeviceImpl::GetDeviceInfo(const GetDeviceInfoCallback& callback) { + callback.Run(info_.Clone()); +} + +} // namespace usb +} // namespace device diff --git a/device/usb/device_impl.h b/device/usb/device_impl.h new file mode 100644 index 0000000..5848c41 --- /dev/null +++ b/device/usb/device_impl.h @@ -0,0 +1,44 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_USB_DEVICE_IMPL_H_ +#define DEVICE_USB_DEVICE_IMPL_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "device/usb/public/interfaces/device.mojom.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" + +namespace device { + +class UsbDevice; + +namespace usb { + +// Implementation of the public Device interface. Instances of this class are +// constructed by DeviceManagerImpl and are strongly bound to their MessagePipe +// lifetime. +class DeviceImpl : public Device { + public: + DeviceImpl(scoped_refptr<UsbDevice> device, + mojo::InterfaceRequest<Device> request); + ~DeviceImpl() override; + + private: + // Device implementation: + void GetDeviceInfo(const GetDeviceInfoCallback& callback) override; + + mojo::StrongBinding<Device> binding_; + + scoped_refptr<UsbDevice> device_; + DeviceInfoPtr info_; + + DISALLOW_COPY_AND_ASSIGN(DeviceImpl); +}; + +} // namespace usb +} // namespace device + +#endif // DEVICE_USB_DEVICE_IMPL_H_ diff --git a/device/usb/device_impl_unittest.cc b/device/usb/device_impl_unittest.cc new file mode 100644 index 0000000..293ffdd7 --- /dev/null +++ b/device/usb/device_impl_unittest.cc @@ -0,0 +1,55 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "device/usb/device_impl.h" +#include "device/usb/mock_usb_device.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" + +namespace device { +namespace usb { + +namespace { + +using DeviceImplTest = testing::Test; + +void ExpectDeviceInfoAndThen(uint16_t vendor_id, + uint16_t product_id, + const std::string& manufacturer, + const std::string& product, + const std::string& serial_number, + const base::Closure& continuation, + DeviceInfoPtr device_info) { + EXPECT_EQ(vendor_id, device_info->vendor_id); + EXPECT_EQ(product_id, device_info->product_id); + EXPECT_EQ(manufacturer, device_info->manufacturer); + EXPECT_EQ(product, device_info->product); + EXPECT_EQ(serial_number, device_info->serial_number); + continuation.Run(); +} + +} // namespace + +// Test that the information returned via the Device::GetDeviceInfo matches that +// of the underlying device. +TEST_F(DeviceImplTest, GetDeviceInfo) { + base::MessageLoop message_loop; + scoped_refptr<MockUsbDevice> fake_device = + new MockUsbDevice(0x1234, 0x5678, "ACME", "Frobinator", "ABCDEF"); + DevicePtr device; + EXPECT_CALL(*fake_device.get(), GetConfiguration()); + new DeviceImpl(fake_device, mojo::GetProxy(&device)); + + base::RunLoop run_loop; + device->GetDeviceInfo(base::Bind(&ExpectDeviceInfoAndThen, 0x1234, 0x5678, + "ACME", "Frobinator", "ABCDEF", + run_loop.QuitClosure())); + run_loop.Run(); +} + +} // namespace usb +} // namespace device diff --git a/device/usb/device_manager_impl.cc b/device/usb/device_manager_impl.cc new file mode 100644 index 0000000..26332b3 --- /dev/null +++ b/device/usb/device_manager_impl.cc @@ -0,0 +1,79 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/usb/device_manager_impl.h" + +#include "base/bind.h" +#include "base/memory/scoped_ptr.h" +#include "device/core/device_client.h" +#include "device/usb/device_impl.h" +#include "device/usb/public/cpp/device_manager_delegate.h" +#include "device/usb/public/cpp/device_manager_factory.h" +#include "device/usb/public/interfaces/device.mojom.h" +#include "device/usb/type_converters.h" +#include "device/usb/usb_device.h" +#include "device/usb/usb_device_filter.h" +#include "device/usb/usb_service.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/array.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" + +namespace device { +namespace usb { + +// static +void DeviceManagerFactory::Build(mojo::InterfaceRequest<DeviceManager> request, + scoped_ptr<DeviceManagerDelegate> delegate) { + // Owned by its MessagePipe. + new DeviceManagerImpl(request.Pass(), delegate.Pass()); +} + +DeviceManagerImpl::DeviceManagerImpl( + mojo::InterfaceRequest<DeviceManager> request, + scoped_ptr<DeviceManagerDelegate> delegate) + : binding_(this, request.Pass()), + delegate_(delegate.Pass()), + weak_factory_(this) { +} + +DeviceManagerImpl::~DeviceManagerImpl() { +} + +void DeviceManagerImpl::GetDevices(EnumerationOptionsPtr options, + const GetDevicesCallback& callback) { + DCHECK(DeviceClient::Get()); + UsbService* usb_service = DeviceClient::Get()->GetUsbService(); + if (!usb_service) { + mojo::Array<EnumerationResultPtr> results(0); + callback.Run(results.Pass()); + return; + } + std::vector<UsbDeviceFilter> filters = + options->filters.To<std::vector<UsbDeviceFilter>>(); + usb_service->GetDevices(base::Bind(&DeviceManagerImpl::OnGetDevices, + weak_factory_.GetWeakPtr(), callback, + filters)); +} + +void DeviceManagerImpl::OnGetDevices( + const GetDevicesCallback& callback, + const std::vector<UsbDeviceFilter>& filters, + const std::vector<scoped_refptr<UsbDevice>>& devices) { + mojo::Array<EnumerationResultPtr> results(0); + for (size_t i = 0; i < devices.size(); ++i) { + DeviceInfoPtr device_info = DeviceInfo::From(*devices[i]); + if (UsbDeviceFilter::MatchesAny(devices[i], filters) && + delegate_->IsDeviceAllowed(*device_info)) { + EnumerationResultPtr result = EnumerationResult::New(); + DevicePtr device; + // Owned by its message pipe. + new DeviceImpl(devices[i], mojo::GetProxy(&device)); + result->device = device.Pass(); + results.push_back(result.Pass()); + } + } + callback.Run(results.Pass()); +} + +} // namespace usb +} // namespace device diff --git a/device/usb/device_manager_impl.h b/device/usb/device_manager_impl.h new file mode 100644 index 0000000..f9a8119 --- /dev/null +++ b/device/usb/device_manager_impl.h @@ -0,0 +1,57 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_USB_DEVICE_MANAGER_IMPL_H_ +#define DEVICE_USB_DEVICE_MANAGER_IMPL_H_ + +#include <vector> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "device/usb/public/interfaces/device_manager.mojom.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" + +namespace device { + +class UsbDevice; +class UsbDeviceFilter; + +namespace usb { + +class DeviceManagerDelegate; + +// Implementation of the public DeviceManager interface. Clients in the browser +// can connect to this service via DeviceClient::ConnectToUSBDeviceManager. +class DeviceManagerImpl : public DeviceManager { + public: + DeviceManagerImpl(mojo::InterfaceRequest<DeviceManager> request, + scoped_ptr<DeviceManagerDelegate> delegate); + ~DeviceManagerImpl() override; + + private: + // DeviceManager implementation: + void GetDevices(EnumerationOptionsPtr options, + const GetDevicesCallback& callback) override; + + // Callback to handle the async response from the underlying UsbService. + void OnGetDevices(const GetDevicesCallback& callback, + const std::vector<UsbDeviceFilter>& filters, + const std::vector<scoped_refptr<UsbDevice>>& devices); + + mojo::StrongBinding<DeviceManager> binding_; + + scoped_ptr<DeviceManagerDelegate> delegate_; + + base::WeakPtrFactory<DeviceManagerImpl> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(DeviceManagerImpl); +}; + +} // namespace usb +} // namespace device + +#endif // DEVICE_USB_DEVICE_MANAGER_IMPL_H_ diff --git a/device/usb/device_manager_impl_unittest.cc b/device/usb/device_manager_impl_unittest.cc new file mode 100644 index 0000000..8c10619 --- /dev/null +++ b/device/usb/device_manager_impl_unittest.cc @@ -0,0 +1,174 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <set> +#include <string> +#include <vector> + +#include "base/barrier_closure.h" +#include "base/bind.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "device/core/device_client.h" +#include "device/usb/device_impl.h" +#include "device/usb/device_manager_impl.h" +#include "device/usb/mock_usb_device.h" +#include "device/usb/mock_usb_service.h" +#include "device/usb/public/cpp/device_manager_delegate.h" +#include "device/usb/public/cpp/device_manager_factory.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" + +namespace device { +namespace usb { + +namespace { + +bool DefaultDelegateFilter(const DeviceInfo& device_info) { + return true; +} + +class TestDeviceManagerDelegate : public DeviceManagerDelegate { + public: + using Filter = base::Callback<bool(const DeviceInfo&)>; + + TestDeviceManagerDelegate(const Filter& filter) : filter_(filter) {} + ~TestDeviceManagerDelegate() override {} + + void set_filter(const Filter& filter) { filter_ = filter; } + + private: + // DeviceManagerDelegate implementation: + bool IsDeviceAllowed(const DeviceInfo& device_info) override { + return filter_.Run(device_info); + } + + Filter filter_; +}; + +class TestDeviceClient : public DeviceClient { + public: + TestDeviceClient() : delegate_filter_(base::Bind(&DefaultDelegateFilter)) {} + ~TestDeviceClient() override {} + + MockUsbService& mock_usb_service() { return mock_usb_service_; } + + void SetDelegateFilter(const TestDeviceManagerDelegate::Filter& filter) { + delegate_filter_ = filter; + } + + private: + // DeviceClient implementation: + UsbService* GetUsbService() override { return &mock_usb_service_; } + + void ConnectToUSBDeviceManager( + mojo::InterfaceRequest<DeviceManager> request) override { + new DeviceManagerImpl(request.Pass(), + scoped_ptr<DeviceManagerDelegate>( + new TestDeviceManagerDelegate(delegate_filter_))); + } + + TestDeviceManagerDelegate::Filter delegate_filter_; + MockUsbService mock_usb_service_; +}; + +class DeviceManagerImplTest : public testing::Test { + public: + DeviceManagerImplTest() + : message_loop_(new base::MessageLoop), + device_client_(new TestDeviceClient) {} + ~DeviceManagerImplTest() override {} + + protected: + MockUsbService& mock_usb_service() { + return device_client_->mock_usb_service(); + } + + void SetDelegateFilter(const TestDeviceManagerDelegate::Filter& filter) { + device_client_->SetDelegateFilter(filter); + } + + private: + scoped_ptr<base::MessageLoop> message_loop_; + scoped_ptr<TestDeviceClient> device_client_; +}; + +void VerifyDevicesAndThen(const std::set<std::string>& expected_serials, + scoped_ptr<std::set<std::string>> actual_serials, + const base::Closure& continuation) { + EXPECT_EQ(expected_serials, *actual_serials); + continuation.Run(); +} + +void OnGetDeviceInfo(std::set<std::string>* actual_serials, + const base::Closure& barrier, + DevicePtr device, + DeviceInfoPtr info) { + actual_serials->insert(info->serial_number.To<std::string>()); + barrier.Run(); +} + +void ExpectDevicesAndThen(const std::set<std::string>& serials, + const base::Closure& continuation, + mojo::Array<EnumerationResultPtr> results) { + EXPECT_EQ(serials.size(), results.size()); + scoped_ptr<std::set<std::string>> actual_serials(new std::set<std::string>); + std::set<std::string>* actual_serials_raw = actual_serials.get(); + base::Closure barrier = base::BarrierClosure( + static_cast<int>(results.size()), + base::Bind(&VerifyDevicesAndThen, serials, base::Passed(&actual_serials), + continuation)); + for (size_t i = 0; i < results.size(); ++i) { + DevicePtr device = results[i]->device.Pass(); + Device* raw_device = device.get(); + raw_device->GetDeviceInfo(base::Bind(&OnGetDeviceInfo, actual_serials_raw, + barrier, base::Passed(&device))); + } +} + +} // namespace + +// Test basic GetDevices functionality to ensure that all mock devices are +// returned by the service. +TEST_F(DeviceManagerImplTest, GetDevices) { + scoped_refptr<MockUsbDevice> device0 = + new MockUsbDevice(0x1234, 0x5678, "ACME", "Frobinator", "ABCDEF"); + scoped_refptr<MockUsbDevice> device1 = + new MockUsbDevice(0x1234, 0x5679, "ACME", "Frobinator+", "GHIJKL"); + scoped_refptr<MockUsbDevice> device2 = + new MockUsbDevice(0x1234, 0x567a, "ACME", "Frobinator Mk II", "MNOPQR"); + + mock_usb_service().AddDevice(device0); + mock_usb_service().AddDevice(device1); + mock_usb_service().AddDevice(device2); + + DeviceManagerPtr device_manager; + DeviceClient::Get()->ConnectToUSBDeviceManager( + mojo::GetProxy(&device_manager)); + + EnumerationOptionsPtr options = EnumerationOptions::New(); + options->filters = mojo::Array<DeviceFilterPtr>::New(1); + options->filters[0] = DeviceFilter::New(); + options->filters[0]->has_vendor_id = true; + options->filters[0]->vendor_id = 0x1234; + + std::set<std::string> serials; + serials.insert("ABCDEF"); + serials.insert("GHIJKL"); + serials.insert("MNOPQR"); + + EXPECT_CALL(*device0.get(), GetConfiguration()); + EXPECT_CALL(*device1.get(), GetConfiguration()); + EXPECT_CALL(*device2.get(), GetConfiguration()); + + base::RunLoop run_loop; + device_manager->GetDevices( + options.Pass(), + base::Bind(&ExpectDevicesAndThen, serials, run_loop.QuitClosure())); + run_loop.Run(); +} + +} // namespace usb +} // namespace device diff --git a/device/usb/public/cpp/BUILD.gn b/device/usb/public/cpp/BUILD.gn new file mode 100644 index 0000000..43cea68 --- /dev/null +++ b/device/usb/public/cpp/BUILD.gn @@ -0,0 +1,16 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("cpp") { + sources = [ + "device_manager_delegate.h", + "device_manager_factory.h", + ] + + public_deps = [ + "//base", + "//device/usb/public/interfaces", + "//third_party/mojo/src/mojo/public/cpp/bindings", + ] +} diff --git a/device/usb/public/cpp/device_manager_delegate.h b/device/usb/public/cpp/device_manager_delegate.h new file mode 100644 index 0000000..306be18 --- /dev/null +++ b/device/usb/public/cpp/device_manager_delegate.h @@ -0,0 +1,27 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_USB_PUBLIC_CPP_DEVICE_MANAGER_DELEGATE_H_ +#define DEVICE_USB_PUBLIC_CPP_DEVICE_MANAGER_DELEGATE_H_ + +#include "device/usb/public/interfaces/device.mojom.h" + +namespace device { +namespace usb { + +// Interface used by DeviceManager instances to delegate certain decisions and +// behaviors out to their embedder. +class DeviceManagerDelegate { + public: + virtual ~DeviceManagerDelegate() {} + + // Determines whether a given device should be accessible to clients of the + // DeviceManager instance. + virtual bool IsDeviceAllowed(const DeviceInfo& info) = 0; +}; + +} // namespace usb +} // namespace device + +#endif // DEVICE_USB_PUBLIC_CPP_DEVICE_MANAGER_DELEGATE_H_ diff --git a/device/usb/public/cpp/device_manager_factory.h b/device/usb/public/cpp/device_manager_factory.h new file mode 100644 index 0000000..51f9727 --- /dev/null +++ b/device/usb/public/cpp/device_manager_factory.h @@ -0,0 +1,34 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_USB_PUBLIC_CPP_DEVICE_MANAGER_FACTORY_H_ +#define DEVICE_USB_PUBLIC_CPP_DEVICE_MANAGER_FACTORY_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "device/usb/public/interfaces/device_manager.mojom.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" + +namespace device { +namespace usb { + +class DeviceManagerDelegate; + +// Public interface to construct an implementation of the DeviceManager service. +class DeviceManagerFactory { + public: + // Builds a new DeviceManager instance to fulfill a request. The service is + // bound to the lifetime of |request|'s MessagePipe, and it takes ownership of + // |delegate|. + static void Build(mojo::InterfaceRequest<DeviceManager> request, + scoped_ptr<DeviceManagerDelegate> delegate); + + private: + DISALLOW_COPY_AND_ASSIGN(DeviceManagerFactory); +}; + +} // namespace usb +} // namespace device + +#endif // DEVICE_USB_PUBLIC_CPP_DEVICE_MANAGER_FACTORY_H_ diff --git a/device/usb/public/interfaces/BUILD.gn b/device/usb/public/interfaces/BUILD.gn new file mode 100644 index 0000000..14b9362 --- /dev/null +++ b/device/usb/public/interfaces/BUILD.gn @@ -0,0 +1,12 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni") + +mojom("interfaces") { + sources = [ + "device.mojom", + "device_manager.mojom", + ] +} diff --git a/device/usb/public/interfaces/device.mojom b/device/usb/public/interfaces/device.mojom new file mode 100644 index 0000000..efb9a1a --- /dev/null +++ b/device/usb/public/interfaces/device.mojom @@ -0,0 +1,61 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module device.usb; + +enum TransferDirection { + IN, + OUT, +}; + +enum EndpointType { + BULK, + INTERRUPT, + ISOCHRONOUS, +}; + +struct EndpointInfo { + uint8 endpoint_number; + TransferDirection direction; + EndpointType type; + uint32 packet_size; +}; + +struct AlternateInterfaceInfo { + uint8 alternate_setting; + uint8 class_code; + uint8 subclass_code; + uint8 protocol_code; + string? interface_name; + array<EndpointInfo> endpoints; +}; + +struct InterfaceInfo { + uint8 interface_number; + array<AlternateInterfaceInfo> alternates; +}; + +struct ConfigurationInfo { + uint8 configuration_value; + string? configuration; + array<InterfaceInfo> interfaces; +}; + +struct DeviceInfo { + uint16 usb_version; + uint8 class_code; + uint8 subclass_code; + uint8 protocol_code; + uint16 vendor_id; + uint16 product_id; + uint16 device_version; + string? manufacturer; + string? product; + string? serial_number; + array<ConfigurationInfo> configurations; +}; + +interface Device { + GetDeviceInfo() => (DeviceInfo info); +}; diff --git a/device/usb/public/interfaces/device_manager.mojom b/device/usb/public/interfaces/device_manager.mojom new file mode 100644 index 0000000..fcf1947 --- /dev/null +++ b/device/usb/public/interfaces/device_manager.mojom @@ -0,0 +1,38 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module device.usb; + +import "device.mojom"; + +struct DeviceFilter { + bool has_vendor_id; + uint16 vendor_id; + + bool has_product_id; + uint16 product_id; + + bool has_class_code; + uint8 class_code; + + bool has_subclass_code; + uint8 subclass_code; + + bool has_protocol_code; + uint8 protocol_code; +}; + +struct EnumerationOptions { + array<DeviceFilter> filters; +}; + +// TODO(rockot): Remove this wrapper struct. Mojom bindings do not yet support +// arrays of interfaces. +struct EnumerationResult { + Device device; +}; + +interface DeviceManager { + GetDevices(EnumerationOptions options) => (array<EnumerationResult> results); +}; diff --git a/device/usb/type_converters.cc b/device/usb/type_converters.cc new file mode 100644 index 0000000..552eb1b --- /dev/null +++ b/device/usb/type_converters.cc @@ -0,0 +1,152 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/usb/type_converters.h" + +#include <map> +#include <utility> + +#include "base/logging.h" +#include "base/strings/utf_string_conversions.h" +#include "device/usb/usb_device.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/array.h" + +namespace mojo { + +// static +device::UsbDeviceFilter +TypeConverter<device::UsbDeviceFilter, device::usb::DeviceFilterPtr>::Convert( + const device::usb::DeviceFilterPtr& mojo_filter) { + device::UsbDeviceFilter filter; + if (mojo_filter->has_vendor_id) + filter.SetVendorId(mojo_filter->vendor_id); + if (mojo_filter->has_product_id) + filter.SetProductId(mojo_filter->product_id); + if (mojo_filter->has_class_code) + filter.SetInterfaceClass(mojo_filter->class_code); + if (mojo_filter->has_subclass_code) + filter.SetInterfaceSubclass(mojo_filter->subclass_code); + if (mojo_filter->has_protocol_code) + filter.SetInterfaceProtocol(mojo_filter->protocol_code); + return filter; +} + +// static +device::usb::TransferDirection +TypeConverter<device::usb::TransferDirection, device::UsbEndpointDirection>:: + Convert(const device::UsbEndpointDirection& direction) { + if (direction == device::USB_DIRECTION_INBOUND) + return device::usb::TRANSFER_DIRECTION_IN; + DCHECK(direction == device::USB_DIRECTION_OUTBOUND); + return device::usb::TRANSFER_DIRECTION_OUT; +} + +// static +device::usb::EndpointType +TypeConverter<device::usb::EndpointType, device::UsbTransferType>::Convert( + const device::UsbTransferType& type) { + switch (type) { + case device::USB_TRANSFER_ISOCHRONOUS: + return device::usb::ENDPOINT_TYPE_ISOCHRONOUS; + case device::USB_TRANSFER_BULK: + return device::usb::ENDPOINT_TYPE_BULK; + case device::USB_TRANSFER_INTERRUPT: + return device::usb::ENDPOINT_TYPE_INTERRUPT; + // Note that we do not expose control transfer in the public interface + // because control endpoints are implied rather than explicitly enumerated + // there. + default: + NOTREACHED(); + return device::usb::ENDPOINT_TYPE_BULK; + } +} + +// static +device::usb::EndpointInfoPtr +TypeConverter<device::usb::EndpointInfoPtr, device::UsbEndpointDescriptor>:: + Convert(const device::UsbEndpointDescriptor& endpoint) { + device::usb::EndpointInfoPtr info = device::usb::EndpointInfo::New(); + info->endpoint_number = endpoint.address; + info->direction = + ConvertTo<device::usb::TransferDirection>(endpoint.direction); + info->type = ConvertTo<device::usb::EndpointType>(endpoint.transfer_type); + info->packet_size = static_cast<uint32_t>(endpoint.maximum_packet_size); + return info.Pass(); +} + +// static +device::usb::AlternateInterfaceInfoPtr +TypeConverter<device::usb::AlternateInterfaceInfoPtr, + device::UsbInterfaceDescriptor>:: + Convert(const device::UsbInterfaceDescriptor& interface) { + device::usb::AlternateInterfaceInfoPtr info = + device::usb::AlternateInterfaceInfo::New(); + info->alternate_setting = interface.alternate_setting; + info->class_code = interface.interface_class; + info->subclass_code = interface.interface_subclass; + info->protocol_code = interface.interface_protocol; + + // Filter out control endpoints for the public interface. + info->endpoints = mojo::Array<device::usb::EndpointInfoPtr>::New(0); + for (const auto& endpoint : interface.endpoints) { + if (endpoint.transfer_type != device::USB_TRANSFER_CONTROL) + info->endpoints.push_back(device::usb::EndpointInfo::From(endpoint)); + } + + return info.Pass(); +} + +// static +mojo::Array<device::usb::InterfaceInfoPtr> +TypeConverter<mojo::Array<device::usb::InterfaceInfoPtr>, + std::vector<device::UsbInterfaceDescriptor>>:: + Convert(const std::vector<device::UsbInterfaceDescriptor>& interfaces) { + auto infos = mojo::Array<device::usb::InterfaceInfoPtr>::New(0); + + // Aggregate each alternate setting into an InterfaceInfo corresponding to its + // interface number. + std::map<uint8_t, device::usb::InterfaceInfo*> interface_map; + for (size_t i = 0; i < interfaces.size(); ++i) { + auto alternate = device::usb::AlternateInterfaceInfo::From(interfaces[i]); + auto iter = interface_map.find(interfaces[i].interface_number); + if (iter == interface_map.end()) { + // This is the first time we're seeing an alternate with this interface + // number, so add a new InterfaceInfo to the array and map the number. + auto info = device::usb::InterfaceInfo::New(); + iter = interface_map.insert(std::make_pair(interfaces[i].interface_number, + info.get())).first; + infos.push_back(info.Pass()); + } + iter->second->alternates.push_back(alternate.Pass()); + } + + return infos.Pass(); +} + +// static +device::usb::ConfigurationInfoPtr +TypeConverter<device::usb::ConfigurationInfoPtr, device::UsbConfigDescriptor>:: + Convert(const device::UsbConfigDescriptor& config) { + device::usb::ConfigurationInfoPtr info = + device::usb::ConfigurationInfo::New(); + info->configuration_value = config.configuration_value; + info->interfaces = + mojo::Array<device::usb::InterfaceInfoPtr>::From(config.interfaces); + return info.Pass(); +} + +// static +device::usb::DeviceInfoPtr +TypeConverter<device::usb::DeviceInfoPtr, device::UsbDevice>::Convert( + const device::UsbDevice& device) { + device::usb::DeviceInfoPtr info = device::usb::DeviceInfo::New(); + info->vendor_id = device.vendor_id(); + info->product_id = device.product_id(); + info->manufacturer = base::UTF16ToUTF8(device.manufacturer_string()); + info->product = base::UTF16ToUTF8(device.product_string()); + info->serial_number = base::UTF16ToUTF8(device.serial_number()); + return info.Pass(); +} + +} // namespace mojo diff --git a/device/usb/type_converters.h b/device/usb/type_converters.h new file mode 100644 index 0000000..861f98e --- /dev/null +++ b/device/usb/type_converters.h @@ -0,0 +1,83 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_USB_TYPE_CONVERTERS_H_ +#define DEVICE_USB_TYPE_CONVERTERS_H_ + +#include <vector> + +#include "device/usb/public/interfaces/device.mojom.h" +#include "device/usb/public/interfaces/device_manager.mojom.h" +#include "device/usb/usb_descriptors.h" +#include "device/usb/usb_device_filter.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/array.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/type_converter.h" + +// Type converters to convert objects between internal device/usb data types and +// public Mojo interface data types. + +namespace device { +class UsbDevice; +} + +namespace mojo { + +template <> +struct TypeConverter<device::UsbDeviceFilter, device::usb::DeviceFilterPtr> { + static device::UsbDeviceFilter Convert( + const device::usb::DeviceFilterPtr& mojo_filter); +}; + +template <> +struct TypeConverter<device::usb::TransferDirection, + device::UsbEndpointDirection> { + static device::usb::TransferDirection Convert( + const device::UsbEndpointDirection& direction); +}; + +template <> +struct TypeConverter<device::usb::EndpointType, device::UsbTransferType> { + static device::usb::EndpointType Convert(const device::UsbTransferType& type); +}; + +template <> +struct TypeConverter<device::usb::EndpointInfoPtr, + device::UsbEndpointDescriptor> { + static device::usb::EndpointInfoPtr Convert( + const device::UsbEndpointDescriptor& endpoint); +}; + +template <> +struct TypeConverter<device::usb::AlternateInterfaceInfoPtr, + device::UsbInterfaceDescriptor> { + static device::usb::AlternateInterfaceInfoPtr Convert( + const device::UsbInterfaceDescriptor& interface); +}; + +// Note that this is an explicit vector-to-array conversion, as +// UsbInterfaceDescriptor collections are flattened to contain all alternate +// settings, whereas InterfaceInfos contain their own sets of alternates with +// a different structure type. +template <> +struct TypeConverter<mojo::Array<device::usb::InterfaceInfoPtr>, + std::vector<device::UsbInterfaceDescriptor>> { + static mojo::Array<device::usb::InterfaceInfoPtr> Convert( + const std::vector<device::UsbInterfaceDescriptor>& interfaces); +}; + +template <> +struct TypeConverter<device::usb::ConfigurationInfoPtr, + device::UsbConfigDescriptor> { + static device::usb::ConfigurationInfoPtr Convert( + const device::UsbConfigDescriptor& config); +}; + +template <> +struct TypeConverter<device::usb::DeviceInfoPtr, device::UsbDevice> { + static device::usb::DeviceInfoPtr Convert(const device::UsbDevice& device); +}; + +} // namespace mojo + +#endif // DEVICE_USB_TYPE_CONVERTERS_H_ diff --git a/device/usb/usb.gyp b/device/usb/usb.gyp index f1afd8c..dc3ebd0 100644 --- a/device/usb/usb.gyp +++ b/device/usb/usb.gyp @@ -11,14 +11,26 @@ 'target_name': 'device_usb', 'type': 'static_library', 'dependencies': [ + 'device_usb_mojo_bindings_lib', '../../components/components.gyp:device_event_log_component', '../../net/net.gyp:net', '../../third_party/libusb/libusb.gyp:libusb', + '../../third_party/mojo/mojo_public.gyp:mojo_cpp_bindings', ], 'include_dirs': [ '../..', ], 'sources': [ + 'device_impl.cc', + 'device_impl.h', + 'device_manager_impl.cc', + 'device_manager_impl.h', + # TODO(rockot/reillyg): Split out public sources into their own target + # once all device/usb consumers have transitioned to the new public API. + 'public/cpp/device_manager_delegate.h', + 'public/cpp/device_manager_factory.h', + 'type_converters.cc', + 'type_converters.h', 'usb_context.cc', 'usb_context.h', 'usb_descriptors.cc', @@ -78,6 +90,26 @@ ] }, { + 'target_name': 'device_usb_mojo_bindings', + 'type': 'none', + 'variables': { + 'mojom_files': [ + 'public/interfaces/device.mojom', + 'public/interfaces/device_manager.mojom', + ], + }, + 'includes': [ + '../../third_party/mojo/mojom_bindings_generator_explicit.gypi', + ], + }, + { + 'target_name': 'device_usb_mojo_bindings_lib', + 'type': 'static_library', + 'dependencies': [ + 'device_usb_mojo_bindings', + ], + }, + { 'target_name': 'device_usb_mocks', 'type': 'static_library', 'include_dirs': [ diff --git a/extensions/shell/browser/shell_device_client.h b/extensions/shell/browser/shell_device_client.h index be3ac30..b3bec78 100644 --- a/extensions/shell/browser/shell_device_client.h +++ b/extensions/shell/browser/shell_device_client.h @@ -17,7 +17,7 @@ namespace extensions { class ShellDeviceClient : device::DeviceClient { public: ShellDeviceClient(); - virtual ~ShellDeviceClient(); + ~ShellDeviceClient() override; // device::DeviceClient implementation device::UsbService* GetUsbService() override; |