Add browser tests for USB device add/remove events.
These tests are essentially copies of the HID device add and remove event tests. In order to reconcile issues with the MockUsbService lifetime it is now tracked by a MessageLoop::DestructionObserver at the generic UsbService level. This is similar to the pattern used by the HidService until it was moved to the UI thread and could use a LazyInstance. BUG=411715 Review URL: https://codereview.chromium.org/800963005 Cr-Commit-Position: refs/heads/master@{#312538}
This commit is contained in:
chrome/browser/extensions/api
device/usb
extensions
browser
api
test
data
api_test
usb
@ -123,20 +123,18 @@ class DevicePermissionsManagerTest : public testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
testing::Test::SetUp();
|
||||
env_.reset(new TestExtensionEnvironment());
|
||||
env_->GetExtensionPrefs(); // Force creation before adding extensions.
|
||||
extension_ =
|
||||
env_->MakeExtension(*base::test::ParseJson(
|
||||
"{"
|
||||
" \"app\": {"
|
||||
" \"background\": {"
|
||||
" \"scripts\": [\"background.js\"]"
|
||||
" }"
|
||||
" },"
|
||||
" \"permissions\": ["
|
||||
" \"usb\""
|
||||
" ]"
|
||||
"}"));
|
||||
env_.GetExtensionPrefs(); // Force creation before adding extensions.
|
||||
extension_ = env_.MakeExtension(*base::test::ParseJson(
|
||||
"{"
|
||||
" \"app\": {"
|
||||
" \"background\": {"
|
||||
" \"scripts\": [\"background.js\"]"
|
||||
" }"
|
||||
" },"
|
||||
" \"permissions\": ["
|
||||
" \"usb\""
|
||||
" ]"
|
||||
"}"));
|
||||
device0_ = new MockUsbDevice("ABCDE");
|
||||
device1_ = new MockUsbDevice("");
|
||||
device2_ = new MockUsbDevice("12345");
|
||||
@ -145,12 +143,7 @@ class DevicePermissionsManagerTest : public testing::Test {
|
||||
UsbService::SetInstanceForTest(usb_service_);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
env_.reset(nullptr);
|
||||
UsbService::SetInstanceForTest(nullptr);
|
||||
}
|
||||
|
||||
scoped_ptr<extensions::TestExtensionEnvironment> env_;
|
||||
extensions::TestExtensionEnvironment env_;
|
||||
const extensions::Extension* extension_;
|
||||
MockUsbService* usb_service_;
|
||||
scoped_refptr<MockUsbDevice> device0_;
|
||||
@ -161,7 +154,7 @@ class DevicePermissionsManagerTest : public testing::Test {
|
||||
|
||||
TEST_F(DevicePermissionsManagerTest, AllowAndClearDevices) {
|
||||
DevicePermissionsManager* manager =
|
||||
DevicePermissionsManager::Get(env_->profile());
|
||||
DevicePermissionsManager::Get(env_.profile());
|
||||
AllowUsbDevice(manager, extension_, device0_);
|
||||
AllowUsbDevice(manager, extension_, device1_);
|
||||
|
||||
@ -205,7 +198,7 @@ TEST_F(DevicePermissionsManagerTest, AllowAndClearDevices) {
|
||||
|
||||
TEST_F(DevicePermissionsManagerTest, SuspendExtension) {
|
||||
DevicePermissionsManager* manager =
|
||||
DevicePermissionsManager::Get(env_->profile());
|
||||
DevicePermissionsManager::Get(env_.profile());
|
||||
AllowUsbDevice(manager, extension_, device0_);
|
||||
AllowUsbDevice(manager, extension_, device1_);
|
||||
|
||||
@ -232,7 +225,7 @@ TEST_F(DevicePermissionsManagerTest, SuspendExtension) {
|
||||
|
||||
TEST_F(DevicePermissionsManagerTest, DisconnectDevice) {
|
||||
DevicePermissionsManager* manager =
|
||||
DevicePermissionsManager::Get(env_->profile());
|
||||
DevicePermissionsManager::Get(env_.profile());
|
||||
AllowUsbDevice(manager, extension_, device0_);
|
||||
AllowUsbDevice(manager, extension_, device1_);
|
||||
|
||||
@ -260,7 +253,7 @@ TEST_F(DevicePermissionsManagerTest, DisconnectDevice) {
|
||||
|
||||
TEST_F(DevicePermissionsManagerTest, RevokeAndRegrantAccess) {
|
||||
DevicePermissionsManager* manager =
|
||||
DevicePermissionsManager::Get(env_->profile());
|
||||
DevicePermissionsManager::Get(env_.profile());
|
||||
AllowUsbDevice(manager, extension_, device0_);
|
||||
AllowUsbDevice(manager, extension_, device1_);
|
||||
|
||||
@ -296,7 +289,7 @@ TEST_F(DevicePermissionsManagerTest, RevokeAndRegrantAccess) {
|
||||
|
||||
TEST_F(DevicePermissionsManagerTest, UpdateLastUsed) {
|
||||
DevicePermissionsManager* manager =
|
||||
DevicePermissionsManager::Get(env_->profile());
|
||||
DevicePermissionsManager::Get(env_.profile());
|
||||
AllowUsbDevice(manager, extension_, device0_);
|
||||
|
||||
scoped_ptr<DevicePermissions> device_permissions =
|
||||
@ -321,11 +314,11 @@ TEST_F(DevicePermissionsManagerTest, LoadPrefs) {
|
||||
" \"vendor_id\": 0"
|
||||
" }"
|
||||
"]");
|
||||
env_->GetExtensionPrefs()->UpdateExtensionPref(extension_->id(), "devices",
|
||||
prefs_value.release());
|
||||
env_.GetExtensionPrefs()->UpdateExtensionPref(extension_->id(), "devices",
|
||||
prefs_value.release());
|
||||
|
||||
DevicePermissionsManager* manager =
|
||||
DevicePermissionsManager::Get(env_->profile());
|
||||
DevicePermissionsManager::Get(env_.profile());
|
||||
scoped_ptr<DevicePermissions> device_permissions =
|
||||
manager->GetForExtension(extension_->id());
|
||||
ASSERT_TRUE(FindEntry(device_permissions.get(), device0_).get());
|
||||
|
@ -25,8 +25,10 @@ source_set("usb") {
|
||||
"usb_error.h",
|
||||
"usb_ids.cc",
|
||||
"usb_ids.h",
|
||||
"usb_service.cc",
|
||||
"usb_service.h",
|
||||
"usb_service_impl.cc",
|
||||
"usb_service_impl.h",
|
||||
generated_ids,
|
||||
]
|
||||
|
||||
|
@ -34,8 +34,10 @@
|
||||
'usb_error.h',
|
||||
'usb_ids.cc',
|
||||
'usb_ids.h',
|
||||
'usb_service.cc',
|
||||
'usb_service.h',
|
||||
'usb_service_impl.cc',
|
||||
'usb_service_impl.h',
|
||||
],
|
||||
'actions': [
|
||||
{
|
||||
|
90
device/usb/usb_service.cc
Normal file
90
device/usb/usb_service.cc
Normal file
@ -0,0 +1,90 @@
|
||||
// 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/usb_service.h"
|
||||
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "device/usb/usb_device.h"
|
||||
#include "device/usb/usb_service_impl.h"
|
||||
|
||||
namespace device {
|
||||
|
||||
namespace {
|
||||
|
||||
UsbService* g_service;
|
||||
|
||||
} // namespace
|
||||
|
||||
// This class manages the lifetime of the global UsbService instance so that
|
||||
// it is destroyed when the current message loop is destroyed. A lazy instance
|
||||
// cannot be used because this object does not live on the main thread.
|
||||
class UsbService::Destroyer : private base::MessageLoop::DestructionObserver {
|
||||
public:
|
||||
explicit Destroyer(UsbService* usb_service) : usb_service_(usb_service) {
|
||||
base::MessageLoop::current()->AddDestructionObserver(this);
|
||||
}
|
||||
~Destroyer() override {}
|
||||
|
||||
private:
|
||||
// base::MessageLoop::DestructionObserver implementation.
|
||||
void WillDestroyCurrentMessageLoop() override {
|
||||
base::MessageLoop::current()->RemoveDestructionObserver(this);
|
||||
delete usb_service_;
|
||||
delete this;
|
||||
g_service = nullptr;
|
||||
}
|
||||
|
||||
UsbService* usb_service_;
|
||||
};
|
||||
|
||||
void UsbService::Observer::OnDeviceAdded(scoped_refptr<UsbDevice> device) {
|
||||
}
|
||||
|
||||
void UsbService::Observer::OnDeviceRemoved(scoped_refptr<UsbDevice> device) {
|
||||
}
|
||||
|
||||
// static
|
||||
UsbService* UsbService::GetInstance(
|
||||
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
|
||||
if (!g_service) {
|
||||
g_service = UsbServiceImpl::Create(ui_task_runner);
|
||||
// This object will clean itself up when the message loop is destroyed.
|
||||
new Destroyer(g_service);
|
||||
}
|
||||
return g_service;
|
||||
}
|
||||
|
||||
// static
|
||||
void UsbService::SetInstanceForTest(UsbService* instance) {
|
||||
g_service = instance;
|
||||
new Destroyer(instance);
|
||||
}
|
||||
|
||||
UsbService::UsbService() {
|
||||
}
|
||||
|
||||
UsbService::~UsbService() {
|
||||
}
|
||||
|
||||
void UsbService::AddObserver(Observer* observer) {
|
||||
DCHECK(CalledOnValidThread());
|
||||
observer_list_.AddObserver(observer);
|
||||
}
|
||||
|
||||
void UsbService::RemoveObserver(Observer* observer) {
|
||||
DCHECK(CalledOnValidThread());
|
||||
observer_list_.RemoveObserver(observer);
|
||||
}
|
||||
|
||||
void UsbService::NotifyDeviceAdded(scoped_refptr<UsbDevice> device) {
|
||||
DCHECK(CalledOnValidThread());
|
||||
FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceAdded(device));
|
||||
}
|
||||
|
||||
void UsbService::NotifyDeviceRemoved(scoped_refptr<UsbDevice> device) {
|
||||
DCHECK(CalledOnValidThread());
|
||||
FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceRemoved(device));
|
||||
}
|
||||
|
||||
} // namespace device
|
@ -55,8 +55,6 @@ class UsbService : public base::NonThreadSafe {
|
||||
void RemoveObserver(Observer* observer);
|
||||
|
||||
protected:
|
||||
friend struct base::DefaultDeleter<UsbService>;
|
||||
|
||||
UsbService();
|
||||
virtual ~UsbService();
|
||||
|
||||
@ -65,6 +63,9 @@ class UsbService : public base::NonThreadSafe {
|
||||
|
||||
ObserverList<Observer, true> observer_list_;
|
||||
|
||||
private:
|
||||
class Destroyer;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(UsbService);
|
||||
};
|
||||
|
||||
|
@ -2,22 +2,17 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "device/usb/usb_service.h"
|
||||
#include "device/usb/usb_service_impl.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/lazy_instance.h"
|
||||
#include "base/location.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/thread_task_runner_handle.h"
|
||||
#include "device/usb/usb_context.h"
|
||||
#include "device/usb/usb_device_impl.h"
|
||||
#include "device/usb/usb_error.h"
|
||||
#include "third_party/libusb/src/libusb/libusb.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <usbiodef.h>
|
||||
@ -28,79 +23,6 @@
|
||||
|
||||
namespace device {
|
||||
|
||||
namespace {
|
||||
|
||||
base::LazyInstance<scoped_ptr<UsbService> >::Leaky g_usb_service_instance =
|
||||
LAZY_INSTANCE_INITIALIZER;
|
||||
|
||||
} // namespace
|
||||
|
||||
typedef struct libusb_device* PlatformUsbDevice;
|
||||
typedef struct libusb_context* PlatformUsbContext;
|
||||
|
||||
class UsbServiceImpl : public UsbService,
|
||||
private base::MessageLoop::DestructionObserver {
|
||||
public:
|
||||
explicit UsbServiceImpl(
|
||||
PlatformUsbContext context,
|
||||
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
|
||||
~UsbServiceImpl() override;
|
||||
|
||||
private:
|
||||
// device::UsbService implementation
|
||||
scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) override;
|
||||
void GetDevices(std::vector<scoped_refptr<UsbDevice>>* devices) override;
|
||||
|
||||
// base::MessageLoop::DestructionObserver implementation.
|
||||
void WillDestroyCurrentMessageLoop() override;
|
||||
|
||||
// Enumerate USB devices from OS and update devices_ map.
|
||||
void RefreshDevices();
|
||||
|
||||
// Adds a new UsbDevice to the devices_ map based on the given libusb device.
|
||||
scoped_refptr<UsbDeviceImpl> AddDevice(PlatformUsbDevice platform_device);
|
||||
|
||||
// Handle hotplug events from libusb.
|
||||
static int LIBUSB_CALL HotplugCallback(libusb_context* context,
|
||||
PlatformUsbDevice device,
|
||||
libusb_hotplug_event event,
|
||||
void* user_data);
|
||||
// These functions release a reference to the provided platform device.
|
||||
void OnDeviceAdded(PlatformUsbDevice platform_device);
|
||||
void OnDeviceRemoved(PlatformUsbDevice platform_device);
|
||||
|
||||
scoped_refptr<UsbContext> context_;
|
||||
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
|
||||
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
class UIThreadHelper;
|
||||
UIThreadHelper* ui_thread_helper_;
|
||||
#endif // OS_WIN
|
||||
|
||||
// TODO(reillyg): Figure out a better solution for device IDs.
|
||||
uint32 next_unique_id_;
|
||||
|
||||
// When available the device list will be updated when new devices are
|
||||
// connected instead of only when a full enumeration is requested.
|
||||
// TODO(reillyg): Support this on all platforms. crbug.com/411715
|
||||
bool hotplug_enabled_;
|
||||
libusb_hotplug_callback_handle hotplug_handle_;
|
||||
|
||||
// The map from unique IDs to UsbDevices.
|
||||
typedef std::map<uint32, scoped_refptr<UsbDeviceImpl> > DeviceMap;
|
||||
DeviceMap devices_;
|
||||
|
||||
// The map from PlatformUsbDevices to UsbDevices.
|
||||
typedef std::map<PlatformUsbDevice, scoped_refptr<UsbDeviceImpl> >
|
||||
PlatformDeviceMap;
|
||||
PlatformDeviceMap platform_devices_;
|
||||
|
||||
base::WeakPtrFactory<UsbServiceImpl> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(UsbServiceImpl);
|
||||
};
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// This class lives on the application main thread so that it can listen for
|
||||
// device change notification window messages. It registers for notifications
|
||||
@ -141,6 +63,23 @@ class UsbServiceImpl::UIThreadHelper final
|
||||
};
|
||||
#endif
|
||||
|
||||
// static
|
||||
UsbService* UsbServiceImpl::Create(
|
||||
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
|
||||
PlatformUsbContext context = NULL;
|
||||
const int rv = libusb_init(&context);
|
||||
if (rv != LIBUSB_SUCCESS) {
|
||||
VLOG(1) << "Failed to initialize libusb: "
|
||||
<< ConvertPlatformUsbErrorToString(rv);
|
||||
return nullptr;
|
||||
}
|
||||
if (!context) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new UsbServiceImpl(context, ui_task_runner);
|
||||
}
|
||||
|
||||
scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) {
|
||||
DCHECK(CalledOnValidThread());
|
||||
RefreshDevices();
|
||||
@ -165,11 +104,6 @@ void UsbServiceImpl::GetDevices(
|
||||
}
|
||||
}
|
||||
|
||||
void UsbServiceImpl::WillDestroyCurrentMessageLoop() {
|
||||
DCHECK(CalledOnValidThread());
|
||||
g_usb_service_instance.Get().reset(NULL);
|
||||
}
|
||||
|
||||
UsbServiceImpl::UsbServiceImpl(
|
||||
PlatformUsbContext context,
|
||||
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
|
||||
@ -178,7 +112,6 @@ UsbServiceImpl::UsbServiceImpl(
|
||||
next_unique_id_(0),
|
||||
hotplug_enabled_(false),
|
||||
weak_factory_(this) {
|
||||
base::MessageLoop::current()->AddDestructionObserver(this);
|
||||
task_runner_ = base::ThreadTaskRunnerHandle::Get();
|
||||
int rv = libusb_hotplug_register_callback(
|
||||
context_->context(),
|
||||
@ -200,7 +133,6 @@ UsbServiceImpl::UsbServiceImpl(
|
||||
}
|
||||
|
||||
UsbServiceImpl::~UsbServiceImpl() {
|
||||
base::MessageLoop::current()->RemoveDestructionObserver(this);
|
||||
if (hotplug_enabled_) {
|
||||
libusb_hotplug_deregister_callback(context_->context(), hotplug_handle_);
|
||||
}
|
||||
@ -356,63 +288,4 @@ void UsbServiceImpl::OnDeviceRemoved(PlatformUsbDevice platform_device) {
|
||||
libusb_unref_device(platform_device);
|
||||
}
|
||||
|
||||
void UsbService::Observer::OnDeviceAdded(scoped_refptr<UsbDevice> device) {
|
||||
}
|
||||
|
||||
void UsbService::Observer::OnDeviceRemoved(scoped_refptr<UsbDevice> device) {
|
||||
}
|
||||
|
||||
// static
|
||||
UsbService* UsbService::GetInstance(
|
||||
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
|
||||
UsbService* instance = g_usb_service_instance.Get().get();
|
||||
if (!instance) {
|
||||
PlatformUsbContext context = NULL;
|
||||
|
||||
const int rv = libusb_init(&context);
|
||||
if (rv != LIBUSB_SUCCESS) {
|
||||
VLOG(1) << "Failed to initialize libusb: "
|
||||
<< ConvertPlatformUsbErrorToString(rv);
|
||||
return NULL;
|
||||
}
|
||||
if (!context)
|
||||
return NULL;
|
||||
|
||||
instance = new UsbServiceImpl(context, ui_task_runner);
|
||||
g_usb_service_instance.Get().reset(instance);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
// static
|
||||
void UsbService::SetInstanceForTest(UsbService* instance) {
|
||||
g_usb_service_instance.Get().reset(instance);
|
||||
}
|
||||
|
||||
UsbService::UsbService() {
|
||||
}
|
||||
|
||||
UsbService::~UsbService() {
|
||||
}
|
||||
|
||||
void UsbService::AddObserver(Observer* observer) {
|
||||
DCHECK(CalledOnValidThread());
|
||||
observer_list_.AddObserver(observer);
|
||||
}
|
||||
|
||||
void UsbService::RemoveObserver(Observer* observer) {
|
||||
DCHECK(CalledOnValidThread());
|
||||
observer_list_.RemoveObserver(observer);
|
||||
}
|
||||
|
||||
void UsbService::NotifyDeviceAdded(scoped_refptr<UsbDevice> device) {
|
||||
DCHECK(CalledOnValidThread());
|
||||
FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceAdded(device));
|
||||
}
|
||||
|
||||
void UsbService::NotifyDeviceRemoved(scoped_refptr<UsbDevice> device) {
|
||||
DCHECK(CalledOnValidThread());
|
||||
FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceRemoved(device));
|
||||
}
|
||||
|
||||
} // namespace device
|
||||
|
82
device/usb/usb_service_impl.h
Normal file
82
device/usb/usb_service_impl.h
Normal file
@ -0,0 +1,82 @@
|
||||
// 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/usb_service.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "device/usb/usb_context.h"
|
||||
#include "device/usb/usb_device_impl.h"
|
||||
#include "third_party/libusb/src/libusb/libusb.h"
|
||||
|
||||
namespace device {
|
||||
|
||||
typedef struct libusb_device* PlatformUsbDevice;
|
||||
typedef struct libusb_context* PlatformUsbContext;
|
||||
|
||||
class UsbServiceImpl : public UsbService {
|
||||
public:
|
||||
static UsbService* Create(
|
||||
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
|
||||
|
||||
private:
|
||||
explicit UsbServiceImpl(
|
||||
PlatformUsbContext context,
|
||||
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
|
||||
~UsbServiceImpl() override;
|
||||
|
||||
// device::UsbService implementation
|
||||
scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) override;
|
||||
void GetDevices(std::vector<scoped_refptr<UsbDevice>>* devices) override;
|
||||
|
||||
// Enumerate USB devices from OS and update devices_ map.
|
||||
void RefreshDevices();
|
||||
|
||||
// Adds a new UsbDevice to the devices_ map based on the given libusb device.
|
||||
scoped_refptr<UsbDeviceImpl> AddDevice(PlatformUsbDevice platform_device);
|
||||
|
||||
// Handle hotplug events from libusb.
|
||||
static int LIBUSB_CALL HotplugCallback(libusb_context* context,
|
||||
PlatformUsbDevice device,
|
||||
libusb_hotplug_event event,
|
||||
void* user_data);
|
||||
// These functions release a reference to the provided platform device.
|
||||
void OnDeviceAdded(PlatformUsbDevice platform_device);
|
||||
void OnDeviceRemoved(PlatformUsbDevice platform_device);
|
||||
|
||||
scoped_refptr<UsbContext> context_;
|
||||
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
|
||||
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
class UIThreadHelper;
|
||||
UIThreadHelper* ui_thread_helper_;
|
||||
#endif // OS_WIN
|
||||
|
||||
// TODO(reillyg): Figure out a better solution for device IDs.
|
||||
uint32 next_unique_id_;
|
||||
|
||||
// When available the device list will be updated when new devices are
|
||||
// connected instead of only when a full enumeration is requested.
|
||||
// TODO(reillyg): Support this on all platforms. crbug.com/411715
|
||||
bool hotplug_enabled_;
|
||||
libusb_hotplug_callback_handle hotplug_handle_;
|
||||
|
||||
// The map from unique IDs to UsbDevices.
|
||||
typedef std::map<uint32, scoped_refptr<UsbDeviceImpl>> DeviceMap;
|
||||
DeviceMap devices_;
|
||||
|
||||
// The map from PlatformUsbDevices to UsbDevices.
|
||||
typedef std::map<PlatformUsbDevice, scoped_refptr<UsbDeviceImpl>>
|
||||
PlatformDeviceMap;
|
||||
PlatformDeviceMap platform_devices_;
|
||||
|
||||
base::WeakPtrFactory<UsbServiceImpl> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(UsbServiceImpl);
|
||||
};
|
||||
|
||||
} // namespace device
|
@ -204,7 +204,7 @@ IN_PROC_BROWSER_TEST_F(HidApiTest, OnDeviceRemoved) {
|
||||
ASSERT_TRUE(LoadApp("api_test/hid/remove_event"));
|
||||
ASSERT_TRUE(load_listener.WaitUntilSatisfied());
|
||||
|
||||
// Device C was not returned by chrome.usb.getDevices, the app will not get
|
||||
// Device C was not returned by chrome.hid.getDevices, the app will not get
|
||||
// a notification.
|
||||
hid_service_->RemoveDevice("C");
|
||||
// Device A was returned, the app will get a notification.
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "device/usb/usb_service.h"
|
||||
#include "extensions/browser/api/usb/usb_api.h"
|
||||
#include "extensions/shell/test/shell_apitest.h"
|
||||
#include "extensions/test/extension_test_message_listener.h"
|
||||
#include "net/base/io_buffer.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
|
||||
@ -126,6 +127,16 @@ class MockUsbService : public UsbService {
|
||||
public:
|
||||
explicit MockUsbService(scoped_refptr<UsbDevice> device) : device_(device) {}
|
||||
|
||||
// Public wrapper around the protected base class method.
|
||||
void NotifyDeviceAdded(scoped_refptr<UsbDevice> device) {
|
||||
UsbService::NotifyDeviceAdded(device);
|
||||
}
|
||||
|
||||
// Public wrapper around the protected base class method.
|
||||
void NotifyDeviceRemoved(scoped_refptr<UsbDevice> device) {
|
||||
UsbService::NotifyDeviceRemoved(device);
|
||||
}
|
||||
|
||||
protected:
|
||||
scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) override {
|
||||
EXPECT_EQ(unique_id, 0U);
|
||||
@ -144,8 +155,12 @@ class UsbApiTest : public ShellApiTest {
|
||||
public:
|
||||
void SetUpOnMainThread() override {
|
||||
ShellApiTest::SetUpOnMainThread();
|
||||
mock_device_handle_ = new MockUsbDeviceHandle();
|
||||
|
||||
mock_device_ = new MockUsbDevice(0, 0, 0);
|
||||
EXPECT_CALL(*mock_device_.get(), GetSerialNumber(_))
|
||||
.WillRepeatedly(Return(false));
|
||||
|
||||
mock_device_handle_ = new MockUsbDeviceHandle();
|
||||
mock_device_handle_->set_device(mock_device_.get());
|
||||
EXPECT_CALL(*mock_device_.get(), RequestUsbAccess(_, _))
|
||||
.WillRepeatedly(Invoke(RequestUsbAccess));
|
||||
@ -160,22 +175,26 @@ class UsbApiTest : public ShellApiTest {
|
||||
}
|
||||
|
||||
void SetUpService() {
|
||||
UsbService::SetInstanceForTest(new MockUsbService(mock_device_));
|
||||
mock_service_ = new MockUsbService(mock_device_);
|
||||
UsbService::SetInstanceForTest(mock_service_);
|
||||
}
|
||||
|
||||
void TearDownOnMainThread() override {
|
||||
UsbService* service = NULL;
|
||||
base::RunLoop run_loop;
|
||||
BrowserThread::PostTaskAndReply(
|
||||
BrowserThread::FILE, FROM_HERE,
|
||||
base::Bind(&UsbService::SetInstanceForTest, service),
|
||||
run_loop.QuitClosure());
|
||||
run_loop.Run();
|
||||
void AddTestDevices() {
|
||||
scoped_refptr<MockUsbDevice> device(new MockUsbDevice(0x18D1, 0x58F0, 1));
|
||||
EXPECT_CALL(*device.get(), GetSerialNumber(_))
|
||||
.WillRepeatedly(Return(false));
|
||||
mock_service_->NotifyDeviceAdded(device);
|
||||
|
||||
device = new MockUsbDevice(0x18D1, 0x58F1, 2);
|
||||
EXPECT_CALL(*device.get(), GetSerialNumber(_))
|
||||
.WillRepeatedly(Return(false));
|
||||
mock_service_->NotifyDeviceAdded(device);
|
||||
}
|
||||
|
||||
protected:
|
||||
scoped_refptr<MockUsbDeviceHandle> mock_device_handle_;
|
||||
scoped_refptr<MockUsbDevice> mock_device_;
|
||||
MockUsbService* mock_service_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@ -263,4 +282,43 @@ IN_PROC_BROWSER_TEST_F(UsbApiTest, InvalidLengthTransfer) {
|
||||
ASSERT_TRUE(RunAppTest("api_test/usb/invalid_length_transfer"));
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(UsbApiTest, OnDeviceAdded) {
|
||||
ExtensionTestMessageListener load_listener("loaded", false);
|
||||
ExtensionTestMessageListener result_listener("success", false);
|
||||
result_listener.set_failure_message("failure");
|
||||
|
||||
ASSERT_TRUE(LoadApp("api_test/usb/add_event"));
|
||||
ASSERT_TRUE(load_listener.WaitUntilSatisfied());
|
||||
|
||||
base::RunLoop run_loop;
|
||||
BrowserThread::PostTaskAndReply(
|
||||
BrowserThread::FILE, FROM_HERE,
|
||||
base::Bind(&UsbApiTest::AddTestDevices, base::Unretained(this)),
|
||||
run_loop.QuitClosure());
|
||||
run_loop.Run();
|
||||
|
||||
ASSERT_TRUE(result_listener.WaitUntilSatisfied());
|
||||
ASSERT_EQ("success", result_listener.message());
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(UsbApiTest, OnDeviceRemoved) {
|
||||
ExtensionTestMessageListener load_listener("loaded", false);
|
||||
ExtensionTestMessageListener result_listener("success", false);
|
||||
result_listener.set_failure_message("failure");
|
||||
|
||||
ASSERT_TRUE(LoadApp("api_test/usb/remove_event"));
|
||||
ASSERT_TRUE(load_listener.WaitUntilSatisfied());
|
||||
|
||||
base::RunLoop run_loop;
|
||||
BrowserThread::PostTaskAndReply(
|
||||
BrowserThread::FILE, FROM_HERE,
|
||||
base::Bind(&MockUsbService::NotifyDeviceRemoved,
|
||||
base::Unretained(mock_service_), mock_device_),
|
||||
run_loop.QuitClosure());
|
||||
run_loop.Run();
|
||||
|
||||
ASSERT_TRUE(result_listener.WaitUntilSatisfied());
|
||||
ASSERT_EQ("success", result_listener.message());
|
||||
}
|
||||
|
||||
} // namespace extensions
|
||||
|
14
extensions/test/data/api_test/usb/add_event/background.js
Normal file
14
extensions/test/data/api_test/usb/add_event/background.js
Normal file
@ -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.
|
||||
|
||||
chrome.usb.onDeviceAdded.addListener(function(device) {
|
||||
if (device.vendorId == 6353 && device.productId == 22768) {
|
||||
chrome.test.sendMessage("success");
|
||||
} else {
|
||||
console.error("Got unexpected device: vid:" + device.vendorId +
|
||||
" pid:" + device.productId);
|
||||
chrome.test.sendMessage("failure");
|
||||
}
|
||||
});
|
||||
chrome.test.sendMessage("loaded");
|
15
extensions/test/data/api_test/usb/add_event/manifest.json
Normal file
15
extensions/test/data/api_test/usb/add_event/manifest.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "chrome.usb.onDeviceAdded",
|
||||
"version": "0.1",
|
||||
"description": "browser test for chrome.usb.onDeviceAdded event",
|
||||
"app": {
|
||||
"background": {
|
||||
"scripts": ["background.js"]
|
||||
}
|
||||
},
|
||||
"permissions": [
|
||||
"usb",
|
||||
// This is a test device emulated by the mocks enabled for the test.
|
||||
{ "usbDevices": [{ "vendorId": 6353, "productId": 22768 }]}
|
||||
]
|
||||
}
|
21
extensions/test/data/api_test/usb/remove_event/background.js
Normal file
21
extensions/test/data/api_test/usb/remove_event/background.js
Normal file
@ -0,0 +1,21 @@
|
||||
// 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.
|
||||
|
||||
var known_devices = {};
|
||||
|
||||
chrome.usb.onDeviceRemoved.addListener(function(device) {
|
||||
if (device.device in known_devices) {
|
||||
chrome.test.sendMessage("success");
|
||||
} else {
|
||||
console.error("Unexpected device removed: " + device.device);
|
||||
chrome.test.sendMessage("failure");
|
||||
}
|
||||
});
|
||||
|
||||
chrome.usb.getDevices({}, function(devices) {
|
||||
for (var device of devices) {
|
||||
known_devices[device.device] = device;
|
||||
}
|
||||
chrome.test.sendMessage("loaded");
|
||||
});
|
15
extensions/test/data/api_test/usb/remove_event/manifest.json
Normal file
15
extensions/test/data/api_test/usb/remove_event/manifest.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "chrome.usb.onDeviceRemoved",
|
||||
"version": "0.1",
|
||||
"description": "browser test for chrome.usb.onDeviceRemoved event",
|
||||
"app": {
|
||||
"background": {
|
||||
"scripts": ["background.js"]
|
||||
}
|
||||
},
|
||||
"permissions": [
|
||||
"usb",
|
||||
// This is a test device emulated by the mocks enabled for the test.
|
||||
{ "usbDevices": [{ "vendorId": 0, "productId": 0 }]}
|
||||
]
|
||||
}
|
Reference in New Issue
Block a user