Commit 89b276fb authored by reillyg's avatar reillyg Committed by Commit bot

Add a service to track devices selected by the user.

apps::SavedDevicesService tracks USB devices that have been selected by
the user in the context of a given extension. Devices that can be
identified accurately after they have been reconnected because they have
a serial number are written out to ExtensionPrefs. All others are only
remembered until they are disconnected.

A new OnDisconnect observer function has been added to UsbDevice to
enable this monitoring.

BUG=346953

Review URL: https://codereview.chromium.org/580963002

Cr-Commit-Position: refs/heads/master@{#295708}
parent bfae6356
...@@ -26,6 +26,10 @@ static_library("apps") { ...@@ -26,6 +26,10 @@ static_library("apps") {
"launcher.cc", "launcher.cc",
"launcher.h", "launcher.h",
"metrics_names.h", "metrics_names.h",
"saved_devices_service.cc",
"saved_devices_service.h",
"saved_devices_service_factory.cc",
"saved_devices_service_factory.h",
"saved_files_service.cc", "saved_files_service.cc",
"saved_files_service.h", "saved_files_service.h",
"saved_files_service_factory.cc", "saved_files_service_factory.cc",
...@@ -41,6 +45,7 @@ static_library("apps") { ...@@ -41,6 +45,7 @@ static_library("apps") {
"//chrome/browser/extensions", "//chrome/browser/extensions",
"//chrome/common/extensions/api:api", "//chrome/common/extensions/api:api",
"//components/web_modal", "//components/web_modal",
"//device/usb",
"//skia", "//skia",
] ]
......
...@@ -9,6 +9,7 @@ include_rules = [ ...@@ -9,6 +9,7 @@ include_rules = [
"+components/sessions", "+components/sessions",
"+components/user_manager", "+components/user_manager",
"+components/web_modal", "+components/web_modal",
"+device/usb",
"+extensions", "+extensions",
"+net/base", "+net/base",
"+skia/ext", "+skia/ext",
......
...@@ -45,6 +45,10 @@ ...@@ -45,6 +45,10 @@
'launcher.cc', 'launcher.cc',
'launcher.h', 'launcher.h',
'metrics_names.h', 'metrics_names.h',
'saved_devices_service.cc',
'saved_devices_service.h',
'saved_devices_service_factory.cc',
'saved_devices_service_factory.h',
'saved_files_service.cc', 'saved_files_service.cc',
'saved_files_service.h', 'saved_files_service.h',
'saved_files_service_factory.cc', 'saved_files_service_factory.cc',
......
// 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.
#include "apps/saved_devices_service.h"
#include <set>
#include <vector>
#include "apps/saved_devices_service_factory.h"
#include "base/basictypes.h"
#include "base/values.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "device/usb/usb_device.h"
#include "device/usb/usb_device_handle.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/extension_util.h"
#include "extensions/browser/notification_types.h"
#include "extensions/common/permissions/api_permission.h"
#include "extensions/common/permissions/permission_set.h"
#include "extensions/common/permissions/permissions_data.h"
namespace apps {
using device::UsbDevice;
using device::UsbDeviceHandle;
using extensions::APIPermission;
using extensions::Extension;
using extensions::ExtensionHost;
using extensions::ExtensionPrefs;
namespace {
// Preference keys
// The device that the app has permission to access.
const char kDevices[] = "devices";
// The type of device saved.
const char kDeviceType[] = "type";
// Type identifier for USB devices.
const char kDeviceTypeUsb[] = "usb";
// The vendor ID of the device that the app had permission to access.
const char kDeviceVendorId[] = "vendor_id";
// The product ID of the device that the app had permission to access.
const char kDeviceProductId[] = "product_id";
// The serial number of the device that the app has permission to access.
const char kDeviceSerialNumber[] = "serial_number";
// Persists a SavedDeviceEntry in ExtensionPrefs.
void AddSavedDeviceEntry(Profile* profile,
const std::string& extension_id,
const SavedDeviceEntry& device) {
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile);
ExtensionPrefs::ScopedListUpdate update(prefs, extension_id, kDevices);
base::ListValue* devices = update.Get();
if (!devices) {
devices = update.Create();
}
base::Value* device_entry = device.ToValue();
DCHECK(devices->Find(*device_entry) == devices->end());
devices->Append(device_entry);
}
// Clears all SavedDeviceEntry for the app from ExtensionPrefs.
void ClearSavedDeviceEntries(ExtensionPrefs* prefs,
const std::string& extension_id) {
prefs->UpdateExtensionPref(extension_id, kDevices, NULL);
}
// Returns all SavedDeviceEntries for the app.
std::vector<SavedDeviceEntry> GetSavedDeviceEntries(
ExtensionPrefs* prefs,
const std::string& extension_id) {
std::vector<SavedDeviceEntry> result;
const base::ListValue* devices = NULL;
if (!prefs->ReadPrefAsList(extension_id, kDevices, &devices)) {
return result;
}
for (base::ListValue::const_iterator it = devices->begin();
it != devices->end();
++it) {
const base::DictionaryValue* device_entry = NULL;
if (!(*it)->GetAsDictionary(&device_entry)) {
continue;
}
int vendor_id;
if (!device_entry->GetIntegerWithoutPathExpansion(kDeviceVendorId,
&vendor_id) ||
vendor_id < 0 || vendor_id > UINT16_MAX) {
continue;
}
int product_id;
if (!device_entry->GetIntegerWithoutPathExpansion(kDeviceProductId,
&product_id) ||
product_id < 0 || product_id > UINT16_MAX) {
continue;
}
base::string16 serial_number;
if (!device_entry->GetStringWithoutPathExpansion(kDeviceSerialNumber,
&serial_number)) {
continue;
}
result.push_back(SavedDeviceEntry(vendor_id, product_id, serial_number));
}
return result;
}
} // namespace
SavedDeviceEntry::SavedDeviceEntry(uint16_t vendor_id,
uint16_t product_id,
const base::string16& serial_number)
: vendor_id(vendor_id),
product_id(product_id),
serial_number(serial_number) {
}
base::Value* SavedDeviceEntry::ToValue() const {
base::DictionaryValue* device_entry_dict = new base::DictionaryValue();
device_entry_dict->SetStringWithoutPathExpansion(kDeviceType, kDeviceTypeUsb);
device_entry_dict->SetIntegerWithoutPathExpansion(kDeviceVendorId, vendor_id);
device_entry_dict->SetIntegerWithoutPathExpansion(kDeviceProductId,
product_id);
device_entry_dict->SetStringWithoutPathExpansion(kDeviceSerialNumber,
serial_number);
return device_entry_dict;
}
bool SavedDevicesService::SavedDevices::IsRegistered(
scoped_refptr<UsbDevice> device) const {
if (ephemeral_devices_.find(device) != ephemeral_devices_.end()) {
return true;
}
bool have_serial_number = false;
base::string16 serial_number;
for (std::vector<SavedDeviceEntry>::const_iterator it =
persistent_devices_.begin();
it != persistent_devices_.end();
++it) {
if (it->vendor_id != device->vendor_id()) {
continue;
}
if (it->product_id != device->product_id()) {
continue;
}
if (!have_serial_number) {
scoped_refptr<UsbDeviceHandle> device_handle = device->Open();
if (!device_handle.get()) {
break;
}
if (!device_handle->GetSerial(&serial_number)) {
break;
}
have_serial_number = true;
}
if (it->serial_number != serial_number) {
continue;
}
return true;
}
return false;
}
void SavedDevicesService::SavedDevices::RegisterDevice(
scoped_refptr<device::UsbDevice> device,
base::string16* serial_number) {
if (serial_number) {
for (std::vector<SavedDeviceEntry>::const_iterator it =
persistent_devices_.begin();
it != persistent_devices_.end();
++it) {
if (it->vendor_id != device->vendor_id()) {
continue;
}
if (it->product_id != device->product_id()) {
continue;
}
if (it->serial_number == *serial_number) {
return;
}
}
SavedDeviceEntry device_entry = SavedDeviceEntry(
device->vendor_id(), device->product_id(), *serial_number);
persistent_devices_.push_back(device_entry);
content::BrowserThread::PostTask(
content::BrowserThread::UI,
FROM_HERE,
base::Bind(AddSavedDeviceEntry, profile_, extension_id_, device_entry));
} else {
// Without a serial number a device cannot be reliably identified when it
// is reconnected so such devices are only remembered until disconnect.
// Register an observer here so that this set doesn't grow undefinitely.
ephemeral_devices_.insert(device);
device->AddObserver(this);
}
}
SavedDevicesService::SavedDevices::SavedDevices(Profile* profile,
const std::string& extension_id)
: profile_(profile), extension_id_(extension_id) {
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
persistent_devices_ = GetSavedDeviceEntries(prefs, extension_id_);
}
SavedDevicesService::SavedDevices::~SavedDevices() {
// Only ephemeral devices have an observer registered.
for (std::set<scoped_refptr<UsbDevice> >::iterator it =
ephemeral_devices_.begin();
it != ephemeral_devices_.end();
++it) {
(*it)->RemoveObserver(this);
}
}
void SavedDevicesService::SavedDevices::OnDisconnect(
scoped_refptr<UsbDevice> device) {
// Permission for an ephemeral device lasts only as long as the device is
// plugged in.
ephemeral_devices_.erase(device);
device->RemoveObserver(this);
}
SavedDevicesService::SavedDevicesService(Profile* profile) : profile_(profile) {
registrar_.Add(this,
extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED,
content::NotificationService::AllSources());
registrar_.Add(this,
chrome::NOTIFICATION_APP_TERMINATING,
content::NotificationService::AllSources());
}
SavedDevicesService::~SavedDevicesService() {
for (std::map<std::string, SavedDevices*>::iterator it =
extension_id_to_saved_devices_.begin();
it != extension_id_to_saved_devices_.end();
++it) {
delete it->second;
}
}
// static
SavedDevicesService* SavedDevicesService::Get(Profile* profile) {
return SavedDevicesServiceFactory::GetForProfile(profile);
}
SavedDevicesService::SavedDevices* SavedDevicesService::GetOrInsert(
const std::string& extension_id) {
SavedDevices* saved_devices = Get(extension_id);
if (saved_devices) {
return saved_devices;
}
saved_devices = new SavedDevices(profile_, extension_id);
extension_id_to_saved_devices_[extension_id] = saved_devices;
return saved_devices;
}
std::vector<SavedDeviceEntry> SavedDevicesService::GetAllDevices(
const std::string& extension_id) const {
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
return GetSavedDeviceEntries(prefs, extension_id);
}
void SavedDevicesService::Clear(const std::string& extension_id) {
DCHECK(thread_checker_.CalledOnValidThread());
ClearSavedDeviceEntries(ExtensionPrefs::Get(profile_), extension_id);
std::map<std::string, SavedDevices*>::iterator it =
extension_id_to_saved_devices_.find(extension_id);
if (it != extension_id_to_saved_devices_.end()) {
delete it->second;
extension_id_to_saved_devices_.erase(it);
}
}
void SavedDevicesService::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
switch (type) {
case extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
Clear(content::Details<ExtensionHost>(details)->extension_id());
break;
}
case chrome::NOTIFICATION_APP_TERMINATING: {
// Stop listening to NOTIFICATION_EXTENSION_HOST_DESTROYED in particular
// as all extension hosts will be destroyed as a result of shutdown.
registrar_.RemoveAll();
break;
}
}
}
SavedDevicesService::SavedDevices* SavedDevicesService::Get(
const std::string& extension_id) const {
DCHECK(thread_checker_.CalledOnValidThread());
std::map<std::string, SavedDevices*>::const_iterator it =
extension_id_to_saved_devices_.find(extension_id);
if (it != extension_id_to_saved_devices_.end()) {
return it->second;
}
return NULL;
}
} // namespace apps
// 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.
#ifndef APPS_SAVED_DEVICES_SERVICE_H_
#define APPS_SAVED_DEVICES_SERVICE_H_
#include <map>
#include <set>
#include <string>
#include <vector>
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/stl_util.h"
#include "base/strings/string16.h"
#include "base/threading/thread_checker.h"
#include "components/keyed_service/core/keyed_service.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "device/usb/usb_device.h"
class Profile;
namespace base {
class Value;
}
namespace extensions {
class Extension;
}
namespace apps {
// Represents a device that a user has given an app permission to access. It
// will be persisted to disk (in the Preferences file) and so should remain
// serializable.
struct SavedDeviceEntry {
SavedDeviceEntry(uint16_t vendor_id,
uint16_t product_id,
const base::string16& serial_number);
base::Value* ToValue() const;
// The vendor ID of this device.
uint16_t vendor_id;
// The product ID of this device.
uint16_t product_id;
// The serial number (possibly alphanumeric) of this device.
base::string16 serial_number;
};
// Tracks the devices that apps have retained access to both while running and
// when suspended.
class SavedDevicesService : public KeyedService,
public content::NotificationObserver {
public:
// Tracks the devices that a particular extension has retained access to.
// Unlike SavedDevicesService the functions of this class can be called from
// the FILE thread.
class SavedDevices : device::UsbDevice::Observer {
public:
bool IsRegistered(scoped_refptr<device::UsbDevice> device) const;
void RegisterDevice(scoped_refptr<device::UsbDevice> device,
/* optional */ base::string16* serial_number);
private:
friend class SavedDevicesService;
SavedDevices(Profile* profile, const std::string& extension_id);
virtual ~SavedDevices();
// device::UsbDevice::Observer
virtual void OnDisconnect(scoped_refptr<device::UsbDevice> device) OVERRIDE;
Profile* profile_;
const std::string extension_id_;
// Devices with serial numbers are written to the prefs file.
std::vector<SavedDeviceEntry> persistent_devices_;
// Other devices are ephemeral devices and cleared when the extension host
// is destroyed.
std::set<scoped_refptr<device::UsbDevice> > ephemeral_devices_;
DISALLOW_COPY_AND_ASSIGN(SavedDevices);
};
explicit SavedDevicesService(Profile* profile);
virtual ~SavedDevicesService();
static SavedDevicesService* Get(Profile* profile);
// Returns the SavedDevices for |extension_id|, creating it if necessary.
SavedDevices* GetOrInsert(const std::string& extension_id);
std::vector<SavedDeviceEntry> GetAllDevices(
const std::string& extension_id) const;
// Clears the SavedDevices for |extension_id|.
void Clear(const std::string& extension_id);
private:
// content::NotificationObserver.
virtual void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE;
// Returns the SavedDevices for |extension_id| or NULL if one does not exist.
SavedDevices* Get(const std::string& extension_id) const;
std::map<std::string, SavedDevices*> extension_id_to_saved_devices_;
Profile* profile_;
content::NotificationRegistrar registrar_;
base::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN(SavedDevicesService);
};
} // namespace apps
#endif // APPS_SAVED_DEVICES_SERVICE_H_
// 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.
#include "apps/saved_devices_service_factory.h"
#include "apps/saved_devices_service.h"
#include "chrome/browser/profiles/profile.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
namespace apps {
// static
SavedDevicesService* SavedDevicesServiceFactory::GetForProfile(
Profile* profile) {
return static_cast<SavedDevicesService*>(
GetInstance()->GetServiceForBrowserContext(profile, true));
}
// static
SavedDevicesServiceFactory* SavedDevicesServiceFactory::GetInstance() {
return Singleton<SavedDevicesServiceFactory>::get();
}
SavedDevicesServiceFactory::SavedDevicesServiceFactory()
: BrowserContextKeyedServiceFactory(
"SavedDevicesService",
BrowserContextDependencyManager::GetInstance()) {
}
SavedDevicesServiceFactory::~SavedDevicesServiceFactory() {
}
KeyedService* SavedDevicesServiceFactory::BuildServiceInstanceFor(
content::BrowserContext* profile) const {
return new SavedDevicesService(static_cast<Profile*>(profile));
}
} // namespace apps
// 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.
#ifndef APPS_SAVED_DEVICES_SERVICE_FACTORY_H_
#define APPS_SAVED_DEVICES_SERVICE_FACTORY_H_
#include "base/memory/singleton.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
class Profile;
namespace apps {
class SavedDevicesService;
// BrowserContextKeyedServiceFactory for SavedDevicesService.
class SavedDevicesServiceFactory : public BrowserContextKeyedServiceFactory {
public:
static SavedDevicesService* GetForProfile(Profile* profile);
static SavedDevicesServiceFactory* GetInstance();
private:
SavedDevicesServiceFactory();
virtual ~SavedDevicesServiceFactory();
friend struct DefaultSingletonTraits<SavedDevicesServiceFactory>;
virtual KeyedService* BuildServiceInstanceFor(
content::BrowserContext* profile) const OVERRIDE;
};
} // namespace apps
#endif // APPS_SAVED_DEVICES_SERVICE_FACTORY_H_
// 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.
#include "apps/saved_devices_service.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/values_test_util.h"
#include "chrome/browser/extensions/test_extension_environment.h"
#include "chrome/test/base/testing_profile.h"
#include "device/usb/usb_device.h"
#include "device/usb/usb_device_handle.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/common/extension.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace apps {
namespace {
using device::UsbDevice;
using device::UsbDeviceHandle;
using device::UsbEndpointDirection;
using device::UsbTransferCallback;
using testing::Return;
class MockUsbDeviceHandle : public UsbDeviceHandle {
public:
MockUsbDeviceHandle(const std::string& serial_number)
: UsbDeviceHandle(), serial_number_(serial_number) {}
MOCK_CONST_METHOD0(GetDevice, scoped_refptr<UsbDevice>());
MOCK_METHOD0(Close, void());
MOCK_METHOD10(ControlTransfer,
void(UsbEndpointDirection direction,
TransferRequestType request_type,
TransferRecipient recipient,
uint8 request,
uint16 value,
uint16 index,
net::IOBuffer* buffer,
size_t length,
unsigned int timeout,
const UsbTransferCallback& callback));
MOCK_METHOD6(BulkTransfer,
void(UsbEndpointDirection direction,
uint8 endpoint,
net::IOBuffer* buffer,
size_t length,
unsigned int timeout,
const UsbTransferCallback& callback));
MOCK_METHOD6(InterruptTransfer,
void(UsbEndpointDirection direction,
uint8 endpoint,
net::IOBuffer* buffer,
size_t length,
unsigned int timeout,
const UsbTransferCallback& callback));
MOCK_METHOD8(IsochronousTransfer,
void(UsbEndpointDirection direction,
uint8 endpoint,
net::IOBuffer* buffer,
size_t length,
unsigned int packets,
unsigned int packet_length,
unsigned int timeout,
const UsbTransferCallback& callback));
MOCK_METHOD0(ResetDevice, bool());
MOCK_METHOD1(ClaimInterface, bool(int interface_number));
MOCK_METHOD1(ReleaseInterface, bool(int interface_number));
MOCK_METHOD2(SetInterfaceAlternateSetting,
bool(int interface_number, int alternate_setting));
MOCK_METHOD1(GetManufacturer, bool(base::string16* manufacturer));
MOCK_METHOD1(GetProduct, bool(base::string16* product));
bool GetSerial(base::string16* serial) OVERRIDE {
if (serial_number_.empty()) {
return false;
}
*serial = base::UTF8ToUTF16(serial_number_);
return true;
}
private:
virtual ~MockUsbDeviceHandle() {}
const std::string serial_number_;
};
class MockUsbDevice : public UsbDevice {
public:
MockUsbDevice(const std::string& serial_number, uint32 unique_id)
: UsbDevice(0, 0, unique_id), serial_number_(serial_number) {}
MOCK_METHOD1(Close, bool(scoped_refptr<UsbDeviceHandle>));
#if defined(OS_CHROMEOS)
MOCK_METHOD2(RequestUsbAccess, void(int, const base::Callback<void(bool)>&));
#endif
MOCK_METHOD0(GetConfiguration, const device::UsbConfigDescriptor&());
scoped_refptr<UsbDeviceHandle> Open() OVERRIDE {
return new MockUsbDeviceHandle(serial_number_);
}
void NotifyDisconnect() { UsbDevice::NotifyDisconnect(); }
private:
virtual ~MockUsbDevice() {}
const std::string serial_number_;
};
}
class SavedDevicesServiceTest : public testing::Test {
protected:
virtual void SetUp() OVERRIDE {
testing::Test::SetUp();
env_.GetExtensionPrefs(); // Force creation before adding extensions.
extension_ = env_.MakeExtension(*base::test::ParseJson(
"{"
" \"app\": {"
" \"background\": {"
" \"scripts\": [\"background.js\"]"
" }"
" },"
" \"permissions\": ["
" \"usb\""
" ]"
"}"));
service_ = SavedDevicesService::Get(env_.profile());
device0 = new MockUsbDevice("ABCDE", 0);
device1 = new MockUsbDevice("", 1);
device2 = new MockUsbDevice("12345", 2);
device3 = new MockUsbDevice("", 3);
}
extensions::TestExtensionEnvironment env_;
const extensions::Extension* extension_;
SavedDevicesService* service_;
scoped_refptr<MockUsbDevice> device0;
scoped_refptr<MockUsbDevice> device1;
scoped_refptr<MockUsbDevice> device2;
scoped_refptr<MockUsbDevice> device3;
};
TEST_F(SavedDevicesServiceTest, RegisterDevices) {
SavedDevicesService::SavedDevices* saved_devices =
service_->GetOrInsert(extension_->id());
base::string16 serial_number(base::ASCIIToUTF16("ABCDE"));
saved_devices->RegisterDevice(device0, &serial_number);
saved_devices->RegisterDevice(device1, NULL);
// This is necessary as writing out registered devices happens in a task on
// the UI thread.
base::RunLoop run_loop;
run_loop.RunUntilIdle();
ASSERT_TRUE(saved_devices->IsRegistered(device0));
ASSERT_TRUE(saved_devices->IsRegistered(device1));
ASSERT_FALSE(saved_devices->IsRegistered(device2));
ASSERT_FALSE(saved_devices->IsRegistered(device3));
std::vector<SavedDeviceEntry> device_entries =
service_->GetAllDevices(extension_->id());
ASSERT_EQ(1U, device_entries.size());
ASSERT_EQ(base::ASCIIToUTF16("ABCDE"), device_entries[0].serial_number);
device1->NotifyDisconnect();
ASSERT_TRUE(saved_devices->IsRegistered(device0));
ASSERT_FALSE(saved_devices->IsRegistered(device1));
ASSERT_FALSE(saved_devices->IsRegistered(device2));
ASSERT_FALSE(saved_devices->IsRegistered(device3));
service_->Clear(extension_->id());
// App is normally restarted, clearing its reference to the SavedDevices.
saved_devices = service_->GetOrInsert(extension_->id());
ASSERT_FALSE(saved_devices->IsRegistered(device0));
device_entries = service_->GetAllDevices(extension_->id());
ASSERT_EQ(0U, device_entries.size());
}
TEST_F(SavedDevicesServiceTest, LoadPrefs) {
scoped_ptr<base::Value> prefs_value = base::test::ParseJson(
"["
" {"
" \"product_id\": 0,"
" \"serial_number\": \"ABCDE\","
" \"type\": \"usb\","
" \"vendor_id\": 0"
" }"
"]");
env_.GetExtensionPrefs()->UpdateExtensionPref(
extension_->id(), "devices", prefs_value.release());
SavedDevicesService::SavedDevices* saved_devices =
service_->GetOrInsert(extension_->id());
ASSERT_TRUE(saved_devices->IsRegistered(device0));
ASSERT_FALSE(saved_devices->IsRegistered(device1));
ASSERT_FALSE(saved_devices->IsRegistered(device2));
ASSERT_FALSE(saved_devices->IsRegistered(device3));
}
} // namespace apps
...@@ -120,7 +120,7 @@ class MockUsbDeviceHandle : public UsbDeviceHandle { ...@@ -120,7 +120,7 @@ class MockUsbDeviceHandle : public UsbDeviceHandle {
virtual void Close() OVERRIDE { device_ = NULL; } virtual void Close() OVERRIDE { device_ = NULL; }
bool ClaimInterface(const int interface_number) { bool ClaimInterface(int interface_number) {
if (device_->claimed_interfaces_.find(interface_number) != if (device_->claimed_interfaces_.find(interface_number) !=
device_->claimed_interfaces_.end()) device_->claimed_interfaces_.end())
return false; return false;
...@@ -129,7 +129,7 @@ class MockUsbDeviceHandle : public UsbDeviceHandle { ...@@ -129,7 +129,7 @@ class MockUsbDeviceHandle : public UsbDeviceHandle {
return true; return true;
} }
bool ReleaseInterface(const int interface_number) { bool ReleaseInterface(int interface_number) {
if (device_->claimed_interfaces_.find(interface_number) == if (device_->claimed_interfaces_.find(interface_number) ==
device_->claimed_interfaces_.end()) device_->claimed_interfaces_.end())
return false; return false;
...@@ -138,9 +138,8 @@ class MockUsbDeviceHandle : public UsbDeviceHandle { ...@@ -138,9 +138,8 @@ class MockUsbDeviceHandle : public UsbDeviceHandle {
return true; return true;
} }
virtual bool SetInterfaceAlternateSetting( virtual bool SetInterfaceAlternateSetting(int interface_number,
const int interface_number, int alternate_setting) OVERRIDE {
const int alternate_setting) OVERRIDE {
return true; return true;
} }
...@@ -162,22 +161,22 @@ class MockUsbDeviceHandle : public UsbDeviceHandle { ...@@ -162,22 +161,22 @@ class MockUsbDeviceHandle : public UsbDeviceHandle {
} }
// Async IO. Can be called on any thread. // Async IO. Can be called on any thread.
virtual void ControlTransfer(const UsbEndpointDirection direction, virtual void ControlTransfer(UsbEndpointDirection direction,
const TransferRequestType request_type, TransferRequestType request_type,
const TransferRecipient recipient, TransferRecipient recipient,
const uint8 request, uint8 request,
const uint16 value, uint16 value,
const uint16 index, uint16 index,
net::IOBuffer* buffer, net::IOBuffer* buffer,
const size_t length, size_t length,
const unsigned int timeout, unsigned int timeout,
const UsbTransferCallback& callback) OVERRIDE {} const UsbTransferCallback& callback) OVERRIDE {}
virtual void BulkTransfer(const UsbEndpointDirection direction, virtual void BulkTransfer(UsbEndpointDirection direction,
const uint8 endpoint, uint8 endpoint,
net::IOBuffer* buffer, net::IOBuffer* buffer,
const size_t length, size_t length,
const unsigned int timeout, unsigned int timeout,
const UsbTransferCallback& callback) OVERRIDE { const UsbTransferCallback& callback) OVERRIDE {
if (direction == device::USB_DIRECTION_OUTBOUND) { if (direction == device::USB_DIRECTION_OUTBOUND) {
if (remaining_body_length_ == 0) { if (remaining_body_length_ == 0) {
...@@ -304,22 +303,22 @@ class MockUsbDeviceHandle : public UsbDeviceHandle { ...@@ -304,22 +303,22 @@ class MockUsbDeviceHandle : public UsbDeviceHandle {
query.size)); query.size));
} }
virtual void InterruptTransfer(const UsbEndpointDirection direction, virtual void InterruptTransfer(UsbEndpointDirection direction,
const uint8 endpoint, uint8 endpoint,
net::IOBuffer* buffer, net::IOBuffer* buffer,
const size_t length, size_t length,
const unsigned int timeout, unsigned int timeout,
const UsbTransferCallback& callback) OVERRIDE { const UsbTransferCallback& callback) OVERRIDE {
} }
virtual void IsochronousTransfer( virtual void IsochronousTransfer(
const UsbEndpointDirection direction, UsbEndpointDirection direction,
const uint8 endpoint, uint8 endpoint,
net::IOBuffer* buffer, net::IOBuffer* buffer,
const size_t length, size_t length,
const unsigned int packets, unsigned int packets,
const unsigned int packet_length, unsigned int packet_length,
const unsigned int timeout, unsigned int timeout,
const UsbTransferCallback& callback) OVERRIDE {} const UsbTransferCallback& callback) OVERRIDE {}
protected: protected:
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
'chrome_unit_tests_sources': [ 'chrome_unit_tests_sources': [
'../apps/app_shim/app_shim_host_mac_unittest.cc', '../apps/app_shim/app_shim_host_mac_unittest.cc',
'../apps/app_shim/extension_app_shim_handler_mac_unittest.cc', '../apps/app_shim/extension_app_shim_handler_mac_unittest.cc',
'../apps/saved_devices_service_unittest.cc',
'../apps/saved_files_service_unittest.cc', '../apps/saved_files_service_unittest.cc',
'../components/autofill/content/renderer/test_password_autofill_agent.cc', '../components/autofill/content/renderer/test_password_autofill_agent.cc',
'../components/autofill/content/renderer/test_password_autofill_agent.h', '../components/autofill/content/renderer/test_password_autofill_agent.h',
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/observer_list.h"
namespace device { namespace device {
...@@ -19,6 +20,11 @@ struct UsbConfigDescriptor; ...@@ -19,6 +20,11 @@ struct UsbConfigDescriptor;
// UsbDeviceHandle must be created from Open() method. // UsbDeviceHandle must be created from Open() method.
class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> { class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> {
public: public:
class Observer {
public:
virtual void OnDisconnect(scoped_refptr<UsbDevice> device) = 0;
};
// Accessors to basic information. // Accessors to basic information.
uint16 vendor_id() const { return vendor_id_; } uint16 vendor_id() const { return vendor_id_; }
uint16 product_id() const { return product_id_; } uint16 product_id() const { return product_id_; }
...@@ -48,11 +54,14 @@ class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> { ...@@ -48,11 +54,14 @@ class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> {
// Blocking method. Must be called on FILE thread. // Blocking method. Must be called on FILE thread.
virtual const UsbConfigDescriptor& GetConfiguration() = 0; virtual const UsbConfigDescriptor& GetConfiguration() = 0;
void AddObserver(Observer* obs) { observer_list_.AddObserver(obs); }
void RemoveObserver(Observer* obs) { observer_list_.RemoveObserver(obs); }
protected: protected:
UsbDevice(uint16 vendor_id, uint16 product_id, uint32 unique_id) UsbDevice(uint16 vendor_id, uint16 product_id, uint32 unique_id);
: vendor_id_(vendor_id), product_id_(product_id), unique_id_(unique_id) {} virtual ~UsbDevice();
virtual ~UsbDevice() {} void NotifyDisconnect();
private: private:
friend class base::RefCountedThreadSafe<UsbDevice>; friend class base::RefCountedThreadSafe<UsbDevice>;
...@@ -61,6 +70,8 @@ class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> { ...@@ -61,6 +70,8 @@ class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> {
const uint16 product_id_; const uint16 product_id_;
const uint32 unique_id_; const uint32 unique_id_;
ObserverList<Observer> observer_list_;
DISALLOW_COPY_AND_ASSIGN(UsbDevice); DISALLOW_COPY_AND_ASSIGN(UsbDevice);
}; };
......
...@@ -51,48 +51,48 @@ class UsbDeviceHandle : public base::RefCountedThreadSafe<UsbDeviceHandle> { ...@@ -51,48 +51,48 @@ class UsbDeviceHandle : public base::RefCountedThreadSafe<UsbDeviceHandle> {
// Device manipulation operations. These methods are blocking and must be // Device manipulation operations. These methods are blocking and must be
// called on FILE thread. // called on FILE thread.
virtual bool ClaimInterface(const int interface_number) = 0; virtual bool ClaimInterface(int interface_number) = 0;
virtual bool ReleaseInterface(const int interface_number) = 0; virtual bool ReleaseInterface(int interface_number) = 0;
virtual bool SetInterfaceAlternateSetting(const int interface_number, virtual bool SetInterfaceAlternateSetting(int interface_number,
const int alternate_setting) = 0; int alternate_setting) = 0;
virtual bool ResetDevice() = 0; virtual bool ResetDevice() = 0;
virtual bool GetManufacturer(base::string16* manufacturer) = 0; virtual bool GetManufacturer(base::string16* manufacturer) = 0;
virtual bool GetProduct(base::string16* product) = 0; virtual bool GetProduct(base::string16* product) = 0;
virtual bool GetSerial(base::string16* serial) = 0; virtual bool GetSerial(base::string16* serial) = 0;
// Async IO. Can be called on any thread. // Async IO. Can be called on any thread.
virtual void ControlTransfer(const UsbEndpointDirection direction, virtual void ControlTransfer(UsbEndpointDirection direction,
const TransferRequestType request_type, TransferRequestType request_type,
const TransferRecipient recipient, TransferRecipient recipient,
const uint8 request, uint8 request,
const uint16 value, uint16 value,
const uint16 index, uint16 index,
net::IOBuffer* buffer, net::IOBuffer* buffer,
const size_t length, size_t length,
const unsigned int timeout, unsigned int timeout,
const UsbTransferCallback& callback) = 0; const UsbTransferCallback& callback) = 0;
virtual void BulkTransfer(const UsbEndpointDirection direction, virtual void BulkTransfer(UsbEndpointDirection direction,
const uint8 endpoint, uint8 endpoint,
net::IOBuffer* buffer, net::IOBuffer* buffer,
const size_t length, size_t length,
const unsigned int timeout, unsigned int timeout,
const UsbTransferCallback& callback) = 0; const UsbTransferCallback& callback) = 0;
virtual void InterruptTransfer(const UsbEndpointDirection direction, virtual void InterruptTransfer(UsbEndpointDirection direction,
const uint8 endpoint, uint8 endpoint,
net::IOBuffer* buffer, net::IOBuffer* buffer,
const size_t length, size_t length,
const unsigned int timeout, unsigned int timeout,
const UsbTransferCallback& callback) = 0; const UsbTransferCallback& callback) = 0;
virtual void IsochronousTransfer(const UsbEndpointDirection direction, virtual void IsochronousTransfer(UsbEndpointDirection direction,
const uint8 endpoint, uint8 endpoint,
net::IOBuffer* buffer, net::IOBuffer* buffer,
const size_t length, size_t length,
const unsigned int packets, unsigned int packets,
const unsigned int packet_length, unsigned int packet_length,
const unsigned int timeout, unsigned int timeout,
const UsbTransferCallback& callback) = 0; const UsbTransferCallback& callback) = 0;
protected: protected:
......
...@@ -35,48 +35,47 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle { ...@@ -35,48 +35,47 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle {
public: public:
virtual scoped_refptr<UsbDevice> GetDevice() const OVERRIDE; virtual scoped_refptr<UsbDevice> GetDevice() const OVERRIDE;
virtual void Close() OVERRIDE; virtual void Close() OVERRIDE;
virtual bool ClaimInterface(const int interface_number) OVERRIDE; virtual bool ClaimInterface(int interface_number) OVERRIDE;
virtual bool ReleaseInterface(const int interface_number) OVERRIDE; virtual bool ReleaseInterface(int interface_number) OVERRIDE;
virtual bool SetInterfaceAlternateSetting( virtual bool SetInterfaceAlternateSetting(int interface_number,
const int interface_number, int alternate_setting) OVERRIDE;
const int alternate_setting) OVERRIDE;
virtual bool ResetDevice() OVERRIDE; virtual bool ResetDevice() OVERRIDE;
virtual bool GetManufacturer(base::string16* manufacturer) OVERRIDE; virtual bool GetManufacturer(base::string16* manufacturer) OVERRIDE;
virtual bool GetProduct(base::string16* product) OVERRIDE; virtual bool GetProduct(base::string16* product) OVERRIDE;
virtual bool GetSerial(base::string16* serial) OVERRIDE; virtual bool GetSerial(base::string16* serial) OVERRIDE;
virtual void ControlTransfer(const UsbEndpointDirection direction, virtual void ControlTransfer(UsbEndpointDirection direction,
const TransferRequestType request_type, TransferRequestType request_type,
const TransferRecipient recipient, TransferRecipient recipient,
const uint8 request, uint8 request,
const uint16 value, uint16 value,
const uint16 index, uint16 index,
net::IOBuffer* buffer, net::IOBuffer* buffer,
const size_t length, size_t length,
const unsigned int timeout, unsigned int timeout,
const UsbTransferCallback& callback) OVERRIDE; const UsbTransferCallback& callback) OVERRIDE;
virtual void BulkTransfer(const UsbEndpointDirection direction, virtual void BulkTransfer(UsbEndpointDirection direction,
const uint8 endpoint, uint8 endpoint,
net::IOBuffer* buffer, net::IOBuffer* buffer,
const size_t length, size_t length,
const unsigned int timeout, unsigned int timeout,
const UsbTransferCallback& callback) OVERRIDE; const UsbTransferCallback& callback) OVERRIDE;
virtual void InterruptTransfer(const UsbEndpointDirection direction, virtual void InterruptTransfer(UsbEndpointDirection direction,
const uint8 endpoint, uint8 endpoint,
net::IOBuffer* buffer, net::IOBuffer* buffer,
const size_t length, size_t length,
const unsigned int timeout, unsigned int timeout,
const UsbTransferCallback& callback) OVERRIDE; const UsbTransferCallback& callback) OVERRIDE;
virtual void IsochronousTransfer( virtual void IsochronousTransfer(
const UsbEndpointDirection direction, UsbEndpointDirection direction,
const uint8 endpoint, uint8 endpoint,
net::IOBuffer* buffer, net::IOBuffer* buffer,
const size_t length, size_t length,
const unsigned int packets, unsigned int packets,
const unsigned int packet_length, unsigned int packet_length,
const unsigned int timeout, unsigned int timeout,
const UsbTransferCallback& callback) OVERRIDE; const UsbTransferCallback& callback) OVERRIDE;
PlatformUsbDeviceHandle handle() const { return handle_; } PlatformUsbDeviceHandle handle() const { return handle_; }
......
...@@ -98,6 +98,17 @@ UsbUsageType GetUsageType(const libusb_endpoint_descriptor* descriptor) { ...@@ -98,6 +98,17 @@ UsbUsageType GetUsageType(const libusb_endpoint_descriptor* descriptor) {
} // namespace } // namespace
UsbDevice::UsbDevice(uint16 vendor_id, uint16 product_id, uint32 unique_id)
: vendor_id_(vendor_id), product_id_(product_id), unique_id_(unique_id) {
}
UsbDevice::~UsbDevice() {
}
void UsbDevice::NotifyDisconnect() {
FOR_EACH_OBSERVER(Observer, observer_list_, OnDisconnect(this));
}
UsbDeviceImpl::UsbDeviceImpl( UsbDeviceImpl::UsbDeviceImpl(
scoped_refptr<UsbContext> context, scoped_refptr<UsbContext> context,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
......
...@@ -45,7 +45,7 @@ class UsbServiceImpl : public UsbService, ...@@ -45,7 +45,7 @@ class UsbServiceImpl : public UsbService,
// base::MessageLoop::DestructionObserver implementation. // base::MessageLoop::DestructionObserver implementation.
virtual void WillDestroyCurrentMessageLoop() OVERRIDE; virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
// Enumerate USB devices from OS and Update devices_ map. // Enumerate USB devices from OS and update devices_ map.
void RefreshDevices(); void RefreshDevices();
scoped_refptr<UsbContext> context_; scoped_refptr<UsbContext> context_;
...@@ -55,19 +55,24 @@ class UsbServiceImpl : public UsbService, ...@@ -55,19 +55,24 @@ class UsbServiceImpl : public UsbService,
// TODO(reillyg): Figure out a better solution. // TODO(reillyg): Figure out a better solution.
uint32 next_unique_id_; uint32 next_unique_id_;
// The map from PlatformUsbDevices to UsbDevices. // The map from unique IDs to UsbDevices.
typedef std::map<PlatformUsbDevice, scoped_refptr<UsbDeviceImpl> > DeviceMap; typedef std::map<uint32, scoped_refptr<UsbDeviceImpl> > DeviceMap;
DeviceMap devices_; DeviceMap devices_;
// The map from PlatformUsbDevices to UsbDevices.
typedef std::map<PlatformUsbDevice, scoped_refptr<UsbDeviceImpl> >
PlatformDeviceMap;
PlatformDeviceMap platform_devices_;
DISALLOW_COPY_AND_ASSIGN(UsbServiceImpl); DISALLOW_COPY_AND_ASSIGN(UsbServiceImpl);
}; };
scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) { scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) {
DCHECK(CalledOnValidThread()); DCHECK(CalledOnValidThread());
RefreshDevices(); RefreshDevices();
for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { DeviceMap::iterator it = devices_.find(unique_id);
if (it->second->unique_id() == unique_id) if (it != devices_.end()) {
return it->second; return it->second;
} }
return NULL; return NULL;
} }
...@@ -120,7 +125,7 @@ void UsbServiceImpl::RefreshDevices() { ...@@ -120,7 +125,7 @@ void UsbServiceImpl::RefreshDevices() {
// Populates new devices. // Populates new devices.
for (ssize_t i = 0; i < device_count; ++i) { for (ssize_t i = 0; i < device_count; ++i) {
if (!ContainsKey(devices_, platform_devices[i])) { if (!ContainsKey(platform_devices_, platform_devices[i])) {
libusb_device_descriptor descriptor; libusb_device_descriptor descriptor;
const int rv = const int rv =
libusb_get_device_descriptor(platform_devices[i], &descriptor); libusb_get_device_descriptor(platform_devices[i], &descriptor);
...@@ -130,31 +135,43 @@ void UsbServiceImpl::RefreshDevices() { ...@@ -130,31 +135,43 @@ void UsbServiceImpl::RefreshDevices() {
<< ConvertPlatformUsbErrorToString(rv); << ConvertPlatformUsbErrorToString(rv);
continue; continue;
} }
UsbDeviceImpl* new_device = new UsbDeviceImpl(context_,
ui_task_runner_, uint32 unique_id;
platform_devices[i], do {
descriptor.idVendor, unique_id = ++next_unique_id_;
descriptor.idProduct, } while (devices_.find(unique_id) != devices_.end());
++next_unique_id_);
devices_[platform_devices[i]] = new_device; scoped_refptr<UsbDeviceImpl> new_device(
connected_devices.insert(new_device); new UsbDeviceImpl(context_,
ui_task_runner_,
platform_devices[i],
descriptor.idVendor,
descriptor.idProduct,
unique_id));
platform_devices_[platform_devices[i]] = new_device;
devices_[unique_id] = new_device;
connected_devices.insert(new_device.get());
} else { } else {
connected_devices.insert(devices_[platform_devices[i]].get()); connected_devices.insert(platform_devices_[platform_devices[i]].get());
} }
} }
// Find disconnected devices. // Find disconnected devices.
for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { for (PlatformDeviceMap::iterator it = platform_devices_.begin();
it != platform_devices_.end();
++it) {
if (!ContainsKey(connected_devices, it->second.get())) { if (!ContainsKey(connected_devices, it->second.get())) {
disconnected_devices.push_back(it->first); disconnected_devices.push_back(it->first);
devices_.erase(it->second->unique_id());
it->second->OnDisconnect();
} }
} }
// Remove disconnected devices from devices_. // Remove disconnected devices from platform_devices_.
for (size_t i = 0; i < disconnected_devices.size(); ++i) { for (size_t i = 0; i < disconnected_devices.size(); ++i) {
// UsbDevice will be destroyed after this. The corresponding // UsbDevice will be destroyed after this. The corresponding
// PlatformUsbDevice will be unref'ed during this process. // PlatformUsbDevice will be unref'ed during this process.
devices_.erase(disconnected_devices[i]); platform_devices_.erase(disconnected_devices[i]);
} }
libusb_free_device_list(platform_devices, true); libusb_free_device_list(platform_devices, true);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment