diff options
author | rockot <rockot@chromium.org> | 2015-06-10 22:53:58 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-06-11 05:54:35 +0000 |
commit | ed800dc138edfa261aa766281b96f736ff9824f6 (patch) | |
tree | acb8cf3be46d24537b8904c9fc2620c0e9b715a2 /device/devices_app | |
parent | c39c3572e353b8120ae64aaf77d32f52598b740e (diff) | |
download | chromium_src-ed800dc138edfa261aa766281b96f736ff9824f6.zip chromium_src-ed800dc138edfa261aa766281b96f736ff9824f6.tar.gz chromium_src-ed800dc138edfa261aa766281b96f736ff9824f6.tar.bz2 |
Introduce the devices Mojo app
It's a small Mojo app that currently only hosts USB device
services. This CL adds the app along with an apptest suite
(run manually with mojo_runner mojo:devices_apptests).
The app is hooked up to Chrome to run in the browser process,
though there are currently no production code paths which
cause the app to launch.
BUG=498557
Review URL: https://codereview.chromium.org/1165223004
Cr-Commit-Position: refs/heads/master@{#333899}
Diffstat (limited to 'device/devices_app')
-rw-r--r-- | device/devices_app/BUILD.gn | 65 | ||||
-rw-r--r-- | device/devices_app/DEPS | 4 | ||||
-rw-r--r-- | device/devices_app/OWNERS | 2 | ||||
-rw-r--r-- | device/devices_app/devices_app.cc | 175 | ||||
-rw-r--r-- | device/devices_app/devices_app.gyp | 34 | ||||
-rw-r--r-- | device/devices_app/devices_app.h | 81 | ||||
-rw-r--r-- | device/devices_app/devices_apptest.cc | 56 | ||||
-rw-r--r-- | device/devices_app/main.cc | 14 |
8 files changed, 431 insertions, 0 deletions
diff --git a/device/devices_app/BUILD.gn b/device/devices_app/BUILD.gn new file mode 100644 index 0000000..fa9bd90 --- /dev/null +++ b/device/devices_app/BUILD.gn @@ -0,0 +1,65 @@ +# 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("//mojo/public/mojo_application.gni") + +source_set("lib") { + sources = [ + "devices_app.cc", + "devices_app.h", + ] + + deps = [ + "//device/core", + "//device/usb", + "//device/usb/public/cpp", + "//device/usb/public/interfaces", + "//third_party/mojo/src/mojo/public/cpp/bindings", + "//url", + ] + + public_deps = [ + "//base", + "//mojo/application/public/cpp", + "//mojo/application/public/interfaces", + ] +} + +mojo_native_application("devices") { + sources = [ + "main.cc", + ] + + deps = [ + ":lib", + "//base", + "//mojo/application/public/cpp", + ] + + public_deps = [ + ":lib", + ] +} + +mojo_native_application("apptests") { + output_name = "devices_apptests" + + testonly = true + + sources = [ + "devices_apptest.cc", + ] + + deps = [ + "//base", + "//mojo/application/public/cpp:test_support", + ] + + public_deps = [ + ":lib", + "//device/usb/public/interfaces", + ] + + data_deps = [ ":devices" ] +} diff --git a/device/devices_app/DEPS b/device/devices_app/DEPS new file mode 100644 index 0000000..8fbdc29 --- /dev/null +++ b/device/devices_app/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+mojo/application/public", + "+mojo/common", +] diff --git a/device/devices_app/OWNERS b/device/devices_app/OWNERS new file mode 100644 index 0000000..b16946a --- /dev/null +++ b/device/devices_app/OWNERS @@ -0,0 +1,2 @@ +reillyg@chromium.org +rockot@chromium.org diff --git a/device/devices_app/devices_app.cc b/device/devices_app/devices_app.cc new file mode 100644 index 0000000..5d54310 --- /dev/null +++ b/device/devices_app/devices_app.cc @@ -0,0 +1,175 @@ +// 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/devices_app/devices_app.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/macros.h" +#include "base/sequenced_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "base/threading/thread.h" +#include "base/time/time.h" +#include "device/core/device_client.h" +#include "device/usb/device_manager_impl.h" +#include "device/usb/public/cpp/device_manager_delegate.h" +#include "device/usb/usb_service.h" +#include "mojo/application/public/cpp/application_connection.h" +#include "mojo/application/public/cpp/application_impl.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/error_handler.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" +#include "url/gurl.h" + +namespace device { + +const char kDevicesMojoAppUrl[] = "system:devices"; + +namespace { + +// The number of seconds to wait without any bound DeviceManagers before +// exiting the app. +const int64 kIdleTimeoutInSeconds = 10; + +// A usb::DeviceManagerDelegate implementation which provides origin-based +// device access control. +class USBDeviceManagerDelegate : public usb::DeviceManagerDelegate { + public: + explicit USBDeviceManagerDelegate(const GURL& remote_url) + : remote_url_(remote_url) {} + ~USBDeviceManagerDelegate() override {} + + private: + // usb::DeviceManagerDelegate: + bool IsDeviceAllowed(const usb::DeviceInfo& device) override { + // Limited set of conditions to allow localhost connection for testing. This + // does not presume to catch all common local host strings. + if (remote_url_.host() == "127.0.0.1" || remote_url_.host() == "localhost") + return true; + + // Also let browser apps and mojo apptests talk to all devices. + if (remote_url_.SchemeIs("system") || + remote_url_ == GURL("mojo://devices_apptests/")) + return true; + + // TODO(rockot/reillyg): Implement origin-based device access control. + return false; + } + + GURL remote_url_; + + DISALLOW_COPY_AND_ASSIGN(USBDeviceManagerDelegate); +}; + +// A DeviceClient implementation to be constructed iff the app is not running +// in an embedder that provides a DeviceClient (i.e. running as a standalone +// Mojo app, not in Chrome). +class AppDeviceClient : public DeviceClient { + public: + explicit AppDeviceClient( + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) + : usb_service_(UsbService::GetInstance(blocking_task_runner)) {} + ~AppDeviceClient() override {} + + private: + // DeviceClient: + UsbService* GetUsbService() override { return usb_service_; } + + UsbService* usb_service_; +}; + +} // namespace + +// This class insures that a UsbService has been initialized and is accessible +// via the DeviceClient interface. +class DevicesApp::USBServiceInitializer { + public: + USBServiceInitializer() + : blocking_thread_("USB service blocking I/O thread") { + blocking_thread_.Start(); + app_device_client_.reset( + new AppDeviceClient(blocking_thread_.task_runner())); + } + + ~USBServiceInitializer() {} + + private: + scoped_ptr<AppDeviceClient> app_device_client_; + base::Thread blocking_thread_; + + DISALLOW_COPY_AND_ASSIGN(USBServiceInitializer); +}; + +DevicesApp::~DevicesApp() { +} + +// static +scoped_ptr<mojo::ApplicationDelegate> DevicesApp::CreateDelegate( + scoped_refptr<base::SequencedTaskRunner> service_task_runner) { + return scoped_ptr<mojo::ApplicationDelegate>( + new DevicesApp(service_task_runner)); +} + +DevicesApp::DevicesApp( + scoped_refptr<base::SequencedTaskRunner> service_task_runner) + : app_impl_(nullptr), + service_task_runner_(service_task_runner), + active_device_manager_count_(0) { +} + +void DevicesApp::Initialize(mojo::ApplicationImpl* app) { + app_impl_ = app; + if (!service_task_runner_) { + service_initializer_.reset(new USBServiceInitializer); + service_task_runner_ = base::ThreadTaskRunnerHandle::Get(); + } + StartIdleTimer(); +} + +bool DevicesApp::ConfigureIncomingConnection( + mojo::ApplicationConnection* connection) { + connection->AddService<usb::DeviceManager>(this); + return true; +} + +void DevicesApp::Quit() { + service_initializer_.reset(); + app_impl_ = nullptr; +} + +void DevicesApp::Create(mojo::ApplicationConnection* connection, + mojo::InterfaceRequest<usb::DeviceManager> request) { + scoped_ptr<usb::DeviceManagerDelegate> delegate(new USBDeviceManagerDelegate( + GURL(connection->GetRemoteApplicationURL()))); + + // Owned by its message pipe. + usb::DeviceManagerImpl* device_manager = new usb::DeviceManagerImpl( + request.Pass(), delegate.Pass(), service_task_runner_); + device_manager->set_error_handler(this); + + active_device_manager_count_++; + idle_timeout_callback_.Cancel(); +} + +void DevicesApp::OnConnectionError() { + DCHECK_GE(active_device_manager_count_, 0u); + active_device_manager_count_--; + if (active_device_manager_count_ == 0) { + // If the last DeviceManager connection has been dropped, kick off an idle + // timeout to shut ourselves down. + StartIdleTimer(); + } +} + +void DevicesApp::StartIdleTimer() { + // Passing unretained |app_impl_| is safe here because |app_impl_| is + // guaranteed to outlive |this|, and the callback is canceled if |this| is + // destroyed. + idle_timeout_callback_.Reset(base::Bind(&mojo::ApplicationImpl::Terminate, + base::Unretained(app_impl_))); + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, idle_timeout_callback_.callback(), + base::TimeDelta::FromSeconds(kIdleTimeoutInSeconds)); +} + +} // namespace device diff --git a/device/devices_app/devices_app.gyp b/device/devices_app/devices_app.gyp new file mode 100644 index 0000000..ac747f3 --- /dev/null +++ b/device/devices_app/devices_app.gyp @@ -0,0 +1,34 @@ +# Copyright 2014 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. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'targets': [ + { + 'target_name': 'devices_app_lib', + 'type': 'static_library', + 'include_dirs': [ + '../..', + ], + 'sources': [ + 'devices_app.cc', + 'devices_app.h', + ], + 'dependencies': [ + '<(DEPTH)/device/usb/usb.gyp:device_usb', + '<(DEPTH)/device/usb/usb.gyp:device_usb_mojo_bindings_lib', + '<(DEPTH)/mojo/mojo_base.gyp:mojo_application_base', + '<(DEPTH)/mojo/mojo_base.gyp:mojo_application_bindings', + '<(DEPTH)/third_party/mojo/mojo_public.gyp:mojo_cpp_bindings', + ], + 'export_dependent_settings': [ + '<(DEPTH)/mojo/mojo_base.gyp:mojo_application_base', + '<(DEPTH)/mojo/mojo_base.gyp:mojo_application_bindings', + '<(DEPTH)/third_party/mojo/mojo_public.gyp:mojo_cpp_bindings', + ], + }, + ], +} diff --git a/device/devices_app/devices_app.h b/device/devices_app/devices_app.h new file mode 100644 index 0000000..4b04568 --- /dev/null +++ b/device/devices_app/devices_app.h @@ -0,0 +1,81 @@ +// 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_DEVICES_DEVICES_APP_H_ +#define DEVICE_DEVICES_DEVICES_APP_H_ + +#include "base/cancelable_callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "mojo/application/public/cpp/application_delegate.h" +#include "mojo/application/public/cpp/interface_factory.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/error_handler.h" + +namespace base { +class SequencedTaskRunner; +} + +namespace mojo { +class ApplicationImpl; +} + +namespace device { + +namespace usb { +class DeviceManager; +} + +extern const char kDevicesMojoAppUrl[]; + +class DevicesApp : public mojo::ApplicationDelegate, + public mojo::InterfaceFactory<usb::DeviceManager>, + public mojo::ErrorHandler { + public: + ~DevicesApp() override; + + // |service_task_runner| is the thread TaskRunner on which the UsbService + // lives. This argument should be removed once UsbService is owned by the + // USB device manager and no longer part of the public device API. If null, + // the app will construct its own DeviceClient and UsbService. + static scoped_ptr<mojo::ApplicationDelegate> CreateDelegate( + scoped_refptr<base::SequencedTaskRunner> service_task_runner); + + private: + class USBServiceInitializer; + + DevicesApp(scoped_refptr<base::SequencedTaskRunner> service_task_runner); + + // mojo::ApplicationDelegate: + void Initialize(mojo::ApplicationImpl* app) override; + bool ConfigureIncomingConnection( + mojo::ApplicationConnection* connection) override; + void Quit() override; + + // mojo::InterfaceFactory<usb::DeviceManager>: + void Create(mojo::ApplicationConnection* connection, + mojo::InterfaceRequest<usb::DeviceManager> request) override; + + // mojo::ErrorHandler: + void OnConnectionError() override; + + // Sets the app for destruction after a period of idle time. If any top-level + // services (e.g. usb::DeviceManager) are bound before the timeout elapses, + // it's canceled. + void StartIdleTimer(); + + mojo::ApplicationImpl* app_impl_; + scoped_ptr<USBServiceInitializer> service_initializer_; + scoped_refptr<base::SequencedTaskRunner> service_task_runner_; + size_t active_device_manager_count_; + + // Callback used to shut down the app after a period of inactivity. + base::CancelableClosure idle_timeout_callback_; + + DISALLOW_COPY_AND_ASSIGN(DevicesApp); +}; + +} // naespace device + +#endif // DEVICE_DEVICES_DEVICES_APP_H_ diff --git a/device/devices_app/devices_apptest.cc b/device/devices_app/devices_apptest.cc new file mode 100644 index 0000000..f466381 --- /dev/null +++ b/device/devices_app/devices_apptest.cc @@ -0,0 +1,56 @@ +// 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/callback.h" +#include "base/macros.h" +#include "base/run_loop.h" +#include "device/devices_app/devices_app.h" +#include "device/usb/public/interfaces/device_manager.mojom.h" +#include "mojo/application/public/cpp/application_impl.h" +#include "mojo/application/public/cpp/application_test_base.h" + +namespace device { +namespace { + +class DevicesAppTest : public mojo::test::ApplicationTestBase { + public: + DevicesAppTest() {} + ~DevicesAppTest() override {} + + void SetUp() override { + ApplicationTestBase::SetUp(); + mojo::URLRequestPtr request = mojo::URLRequest::New(); + request->url = "mojo:devices"; + application_impl()->ConnectToService(request.Pass(), &usb_device_manager_); + } + + usb::DeviceManager* usb_device_manager() { return usb_device_manager_.get(); } + + private: + usb::DeviceManagerPtr usb_device_manager_; + + DISALLOW_COPY_AND_ASSIGN(DevicesAppTest); +}; + +void OnGetDevices(const base::Closure& continuation, + mojo::Array<usb::DeviceInfoPtr> devices) { + continuation.Run(); +} + +} // namespace + +// Simple test to verify that we can connect to the USB DeviceManager and get +// a response. +TEST_F(DevicesAppTest, GetUSBDevices) { + base::RunLoop loop; + usb::EnumerationOptionsPtr options = usb::EnumerationOptions::New(); + options->filters = mojo::Array<usb::DeviceFilterPtr>(1); + options->filters[0] = usb::DeviceFilter::New(); + usb_device_manager()->GetDevices( + options.Pass(), base::Bind(&OnGetDevices, loop.QuitClosure())); + loop.Run(); +} + +} // namespace device diff --git a/device/devices_app/main.cc b/device/devices_app/main.cc new file mode 100644 index 0000000..e82edb3 --- /dev/null +++ b/device/devices_app/main.cc @@ -0,0 +1,14 @@ +// 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/sequenced_task_runner.h" +#include "device/devices_app/devices_app.h" +#include "mojo/application/public/cpp/application_runner.h" +#include "third_party/mojo/src/mojo/public/c/system/main.h" + +MojoResult MojoMain(MojoHandle shell_handle) { + mojo::ApplicationRunner runner( + device::DevicesApp::CreateDelegate(nullptr).release()); + return runner.Run(shell_handle); +} |