summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrockot <rockot@chromium.org>2015-05-27 17:42:53 -0700
committerCommit bot <commit-bot@chromium.org>2015-05-28 00:43:26 +0000
commit249cc25c5e0a2e338cd3efec6e1a3964e7c983e9 (patch)
treee156ae25c3b6c092cd6075565c44230bb97a2091
parentd6e6531d7c1449e06d8768f11b8a8d2f357dc220 (diff)
downloadchromium_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}
-rw-r--r--chrome/browser/OWNERS3
-rw-r--r--chrome/browser/chrome_device_client.cc30
-rw-r--r--chrome/browser/chrome_device_client.h4
-rw-r--r--device/BUILD.gn5
-rw-r--r--device/core/BUILD.gn3
-rw-r--r--device/core/core.gyp3
-rw-r--r--device/core/device_client.cc4
-rw-r--r--device/core/device_client.h12
-rw-r--r--device/device_tests.gyp2
-rw-r--r--device/usb/BUILD.gn8
-rw-r--r--device/usb/device_impl.cc34
-rw-r--r--device/usb/device_impl.h44
-rw-r--r--device/usb/device_impl_unittest.cc55
-rw-r--r--device/usb/device_manager_impl.cc79
-rw-r--r--device/usb/device_manager_impl.h57
-rw-r--r--device/usb/device_manager_impl_unittest.cc174
-rw-r--r--device/usb/public/cpp/BUILD.gn16
-rw-r--r--device/usb/public/cpp/device_manager_delegate.h27
-rw-r--r--device/usb/public/cpp/device_manager_factory.h34
-rw-r--r--device/usb/public/interfaces/BUILD.gn12
-rw-r--r--device/usb/public/interfaces/device.mojom61
-rw-r--r--device/usb/public/interfaces/device_manager.mojom38
-rw-r--r--device/usb/type_converters.cc152
-rw-r--r--device/usb/type_converters.h83
-rw-r--r--device/usb/usb.gyp32
-rw-r--r--extensions/shell/browser/shell_device_client.h2
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;