summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorreillyg <reillyg@chromium.org>2014-09-29 12:27:54 -0700
committerCommit bot <commit-bot@chromium.org>2014-09-29 19:28:09 +0000
commitbc82d3c46cedec68b175c9572a7de412eb0e6427 (patch)
tree5d012e638adbb770f9bc5eb4bf7a60ed88b8dc2f
parente01e6de4a23e8c9e53c497346d584e7f509e59f2 (diff)
downloadchromium_src-bc82d3c46cedec68b175c9572a7de412eb0e6427.zip
chromium_src-bc82d3c46cedec68b175c9572a7de412eb0e6427.tar.gz
chromium_src-bc82d3c46cedec68b175c9572a7de412eb0e6427.tar.bz2
Rewrite apps::SavedDevicesService as extensions::DevicePermissionsManager.
This service doesn't need any Chrome dependencies after all and so it can move into //extensions. This will make it possible for the USB extensions API code to directly depend on it. In the process I have rewritten the service with a couple improvements: * C++11 for-each loops are used instead of explicit iterators. * The DevicePermissions object is no longer an inner class (so it can be forward declared) and it is explicitly copied when requested so that the ownership of data on the FILE and UI threads is clear. BUG= Review URL: https://codereview.chromium.org/606503002 Cr-Commit-Position: refs/heads/master@{#297230}
-rw-r--r--apps/BUILD.gn5
-rw-r--r--apps/DEPS1
-rw-r--r--apps/apps.gypi4
-rw-r--r--apps/saved_devices_service.cc316
-rw-r--r--apps/saved_devices_service.h123
-rw-r--r--apps/saved_devices_service_factory.cc39
-rw-r--r--apps/saved_devices_service_factory.h35
-rw-r--r--chrome/browser/extensions/api/device_permissions_manager_unittest.cc (renamed from apps/saved_devices_service_unittest.cc)96
-rw-r--r--chrome/chrome_tests_unit.gypi2
-rw-r--r--extensions/browser/BUILD.gn2
-rw-r--r--extensions/browser/DEPS1
-rw-r--r--extensions/browser/api/device_permissions_manager.cc384
-rw-r--r--extensions/browser/api/device_permissions_manager.h151
-rw-r--r--extensions/browser/api/usb/DEPS1
-rw-r--r--extensions/browser/api/usb_private/DEPS1
-rw-r--r--extensions/extensions.gyp2
-rw-r--r--extensions/extensions_strings.grd9
17 files changed, 596 insertions, 576 deletions
diff --git a/apps/BUILD.gn b/apps/BUILD.gn
index 4070818..d46feaa 100644
--- a/apps/BUILD.gn
+++ b/apps/BUILD.gn
@@ -26,10 +26,6 @@ static_library("apps") {
"launcher.cc",
"launcher.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.h",
"saved_files_service_factory.cc",
@@ -45,7 +41,6 @@ static_library("apps") {
"//chrome/browser/extensions",
"//chrome/common/extensions/api:api",
"//components/web_modal",
- "//device/usb",
"//skia",
]
diff --git a/apps/DEPS b/apps/DEPS
index 01144ad..e2f3d69 100644
--- a/apps/DEPS
+++ b/apps/DEPS
@@ -9,7 +9,6 @@ include_rules = [
"+components/sessions",
"+components/user_manager",
"+components/web_modal",
- "+device/usb",
"+extensions",
"+net/base",
"+skia/ext",
diff --git a/apps/apps.gypi b/apps/apps.gypi
index 1b464ae..cb8478d 100644
--- a/apps/apps.gypi
+++ b/apps/apps.gypi
@@ -45,10 +45,6 @@
'launcher.cc',
'launcher.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.h',
'saved_files_service_factory.cc',
diff --git a/apps/saved_devices_service.cc b/apps/saved_devices_service.cc
deleted file mode 100644
index d7e894c..0000000
--- a/apps/saved_devices_service.cc
+++ /dev/null
@@ -1,316 +0,0 @@
-// 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) {
- if (!device->GetSerialNumber(&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
diff --git a/apps/saved_devices_service.h b/apps/saved_devices_service.h
deleted file mode 100644
index 52a1874..0000000
--- a/apps/saved_devices_service.h
+++ /dev/null
@@ -1,123 +0,0 @@
-// 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_
diff --git a/apps/saved_devices_service_factory.cc b/apps/saved_devices_service_factory.cc
deleted file mode 100644
index 1153eac..0000000
--- a/apps/saved_devices_service_factory.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// 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
diff --git a/apps/saved_devices_service_factory.h b/apps/saved_devices_service_factory.h
deleted file mode 100644
index d1b39a1..0000000
--- a/apps/saved_devices_service_factory.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// 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_
diff --git a/apps/saved_devices_service_unittest.cc b/chrome/browser/extensions/api/device_permissions_manager_unittest.cc
index b48e8a6..3990933 100644
--- a/apps/saved_devices_service_unittest.cc
+++ b/chrome/browser/extensions/api/device_permissions_manager_unittest.cc
@@ -2,7 +2,6 @@
// 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"
@@ -10,19 +9,18 @@
#include "chrome/test/base/testing_profile.h"
#include "device/usb/usb_device.h"
#include "device/usb/usb_device_handle.h"
+#include "extensions/browser/api/device_permissions_manager.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 extensions {
namespace {
using device::UsbDevice;
using device::UsbDeviceHandle;
-using device::UsbEndpointDirection;
-using device::UsbTransferCallback;
using testing::Return;
class MockUsbDevice : public UsbDevice {
@@ -39,12 +37,12 @@ class MockUsbDevice : public UsbDevice {
MOCK_METHOD1(GetManufacturer, bool(base::string16*));
MOCK_METHOD1(GetProduct, bool(base::string16*));
- bool GetSerialNumber(base::string16* serial) OVERRIDE {
+ virtual bool GetSerialNumber(base::string16* serial_number) OVERRIDE {
if (serial_number_.empty()) {
return false;
}
- *serial = base::UTF8ToUTF16(serial_number_);
+ *serial_number = base::UTF8ToUTF16(serial_number_);
return true;
}
@@ -55,9 +53,10 @@ class MockUsbDevice : public UsbDevice {
const std::string serial_number_;
};
-}
-class SavedDevicesServiceTest : public testing::Test {
+} // namespace
+
+class DevicePermissionsManagerTest : public testing::Test {
protected:
virtual void SetUp() OVERRIDE {
testing::Test::SetUp();
@@ -73,7 +72,6 @@ class SavedDevicesServiceTest : public testing::Test {
" \"usb\""
" ]"
"}"));
- service_ = SavedDevicesService::Get(env_.profile());
device0 = new MockUsbDevice("ABCDE", 0);
device1 = new MockUsbDevice("", 1);
device2 = new MockUsbDevice("12345", 2);
@@ -82,53 +80,49 @@ class SavedDevicesServiceTest : public testing::Test {
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);
+TEST_F(DevicePermissionsManagerTest, RegisterDevices) {
+ DevicePermissionsManager* manager =
+ DevicePermissionsManager::Get(env_.profile());
+ manager->AllowUsbDevice(
+ extension_->id(), device0, base::ASCIIToUTF16("ABCDE"));
+ manager->AllowUsbDevice(extension_->id(), device1, base::string16());
+
+ scoped_ptr<DevicePermissions> device_permissions =
+ manager->GetForExtension(extension_->id());
+ ASSERT_TRUE(device_permissions->CheckUsbDevice(device0));
+ ASSERT_TRUE(device_permissions->CheckUsbDevice(device1));
+ ASSERT_FALSE(device_permissions->CheckUsbDevice(device2));
+ ASSERT_FALSE(device_permissions->CheckUsbDevice(device3));
+
+ std::vector<base::string16> device_messages =
+ manager->GetPermissionMessageStrings(extension_->id());
+ ASSERT_EQ(1U, device_messages.size());
+ ASSERT_NE(device_messages[0].find(base::ASCIIToUTF16("ABCDE")),
+ base::string16::npos);
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));
+ device_permissions = manager->GetForExtension(extension_->id());
+ ASSERT_TRUE(device_permissions->CheckUsbDevice(device0));
+ ASSERT_FALSE(device_permissions->CheckUsbDevice(device1));
+ ASSERT_FALSE(device_permissions->CheckUsbDevice(device2));
+ ASSERT_FALSE(device_permissions->CheckUsbDevice(device3));
- service_->Clear(extension_->id());
+ manager->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());
+ device_permissions = manager->GetForExtension(extension_->id());
+ ASSERT_FALSE(device_permissions->CheckUsbDevice(device0));
+ device_messages = manager->GetPermissionMessageStrings(extension_->id());
+ ASSERT_EQ(0U, device_messages.size());
}
-TEST_F(SavedDevicesServiceTest, LoadPrefs) {
+TEST_F(DevicePermissionsManagerTest, LoadPrefs) {
scoped_ptr<base::Value> prefs_value = base::test::ParseJson(
"["
" {"
@@ -141,12 +135,14 @@ TEST_F(SavedDevicesServiceTest, LoadPrefs) {
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));
+ DevicePermissionsManager* manager =
+ DevicePermissionsManager::Get(env_.profile());
+ scoped_ptr<DevicePermissions> device_permissions =
+ manager->GetForExtension(extension_->id());
+ ASSERT_TRUE(device_permissions->CheckUsbDevice(device0));
+ ASSERT_FALSE(device_permissions->CheckUsbDevice(device1));
+ ASSERT_FALSE(device_permissions->CheckUsbDevice(device2));
+ ASSERT_FALSE(device_permissions->CheckUsbDevice(device3));
}
-} // namespace apps
+} // namespace extensions
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index d982db2..ffff874 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -4,7 +4,6 @@
{
'variables': {
'chrome_unit_tests_sources': [
- '../apps/saved_devices_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.h',
@@ -317,6 +316,7 @@
'browser/extensions/api/declarative_content/chrome_content_rules_registry_unittest.cc',
'browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc',
'browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc',
+ 'browser/extensions/api/device_permissions_manager_unittest.cc',
'browser/extensions/api/dial/dial_device_data_unittest.cc',
'browser/extensions/api/dial/dial_registry_unittest.cc',
'browser/extensions/api/dial/dial_service_unittest.cc',
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 0b7e062..55dbed7 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -122,6 +122,8 @@ source_set("browser") {
"api/declarative_webrequest/webrequest_constants.h",
"api/declarative_webrequest/webrequest_rules_registry.cc",
"api/declarative_webrequest/webrequest_rules_registry.h",
+ "api/device_permissions_manager.cc",
+ "api/device_permissions_manager.h",
"api/dns/dns_api.cc",
"api/dns/dns_api.h",
"api/dns/host_resolver_wrapper.cc",
diff --git a/extensions/browser/DEPS b/extensions/browser/DEPS
index e540b13..c1d5be8 100644
--- a/extensions/browser/DEPS
+++ b/extensions/browser/DEPS
@@ -7,6 +7,7 @@ include_rules = [
"+components/web_modal",
"+content/public/browser",
"+device/bluetooth",
+ "+device/usb",
"+grit/extensions_strings.h",
"+net",
"+skia/ext/image_operations.h",
diff --git a/extensions/browser/api/device_permissions_manager.cc b/extensions/browser/api/device_permissions_manager.cc
new file mode 100644
index 0000000..a005adb
--- /dev/null
+++ b/extensions/browser/api/device_permissions_manager.cc
@@ -0,0 +1,384 @@
+// 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 "extensions/browser/api/device_permissions_manager.h"
+
+#include "base/memory/singleton.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "content/public/browser/notification_service.h"
+#include "device/usb/usb_ids.h"
+#include "extensions/browser/extension_host.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/notification_types.h"
+#include "extensions/strings/grit/extensions_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace extensions {
+
+using content::BrowserContext;
+using device::UsbDevice;
+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 DevicePermissionEntry in ExtensionPrefs.
+void SaveDevicePermissionEntry(BrowserContext* context,
+ const std::string& extension_id,
+ const DevicePermissionEntry& device) {
+ ExtensionPrefs* prefs = ExtensionPrefs::Get(context);
+ 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 DevicePermissionEntries for the app from ExtensionPrefs.
+void ClearDevicePermissionEntries(ExtensionPrefs* prefs,
+ const std::string& extension_id) {
+ prefs->UpdateExtensionPref(extension_id, kDevices, NULL);
+}
+
+// Returns all DevicePermissionEntries for the app.
+std::vector<DevicePermissionEntry> GetDevicePermissionEntries(
+ ExtensionPrefs* prefs,
+ const std::string& extension_id) {
+ std::vector<DevicePermissionEntry> 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(
+ DevicePermissionEntry(vendor_id, product_id, serial_number));
+ }
+ return result;
+}
+}
+
+DevicePermissionEntry::DevicePermissionEntry(
+ 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* DevicePermissionEntry::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;
+}
+
+DevicePermissions::~DevicePermissions() {
+}
+
+bool DevicePermissions::CheckUsbDevice(
+ scoped_refptr<device::UsbDevice> device) const {
+ if (ephemeral_devices_.find(device) != ephemeral_devices_.end()) {
+ return true;
+ }
+
+ bool have_serial_number = false;
+ base::string16 serial_number;
+ for (const auto& entry : permission_entries_) {
+ if (entry.vendor_id != device->vendor_id()) {
+ continue;
+ }
+ if (entry.product_id != device->product_id()) {
+ continue;
+ }
+ if (!have_serial_number) {
+ if (!device->GetSerialNumber(&serial_number)) {
+ break;
+ }
+ have_serial_number = true;
+ }
+ if (entry.serial_number != serial_number) {
+ continue;
+ }
+ return true;
+ }
+ return false;
+}
+
+DevicePermissions::DevicePermissions(BrowserContext* context,
+ const std::string& extension_id) {
+ ExtensionPrefs* prefs = ExtensionPrefs::Get(context);
+ permission_entries_ = GetDevicePermissionEntries(prefs, extension_id);
+}
+
+DevicePermissions::DevicePermissions(
+ const std::vector<DevicePermissionEntry>& permission_entries,
+ const std::set<scoped_refptr<device::UsbDevice>>& ephemeral_devices)
+ : permission_entries_(permission_entries),
+ ephemeral_devices_(ephemeral_devices) {
+}
+
+std::vector<DevicePermissionEntry>& DevicePermissions::permission_entries() {
+ return permission_entries_;
+}
+
+std::set<scoped_refptr<device::UsbDevice>>&
+DevicePermissions::ephemeral_devices() {
+ return ephemeral_devices_;
+}
+
+// static
+DevicePermissionsManager* DevicePermissionsManager::Get(
+ BrowserContext* context) {
+ return DevicePermissionsManagerFactory::GetForBrowserContext(context);
+}
+
+scoped_ptr<DevicePermissions> DevicePermissionsManager::GetForExtension(
+ const std::string& extension_id) {
+ DCHECK(CalledOnValidThread());
+
+ DevicePermissions* device_permissions = GetOrInsert(extension_id);
+ return make_scoped_ptr(
+ new DevicePermissions(device_permissions->permission_entries(),
+ device_permissions->ephemeral_devices()));
+}
+
+std::vector<base::string16>
+DevicePermissionsManager::GetPermissionMessageStrings(
+ const std::string& extension_id) {
+ DCHECK(CalledOnValidThread());
+
+ std::vector<base::string16> messages;
+ DevicePermissions* device_permissions = Get(extension_id);
+ if (!device_permissions) {
+ return messages;
+ }
+
+ for (const auto& entry : device_permissions->permission_entries()) {
+ const char* vendorName = device::UsbIds::GetVendorName(entry.vendor_id);
+ const char* productName =
+ device::UsbIds::GetProductName(entry.vendor_id, entry.product_id);
+ if (vendorName) {
+ if (productName) {
+ messages.push_back(l10n_util::GetStringFUTF16(
+ IDS_EXTENSION_PROMPT_WARNING_USB_DEVICE_SERIAL,
+ base::UTF8ToUTF16(vendorName),
+ base::UTF8ToUTF16(productName),
+ entry.serial_number));
+ } else {
+ messages.push_back(l10n_util::GetStringFUTF16(
+ IDS_EXTENSION_PROMPT_WARNING_USB_DEVICE_PID_SERIAL,
+ base::UTF8ToUTF16(vendorName),
+ base::ASCIIToUTF16(base::StringPrintf("0x%04X", entry.product_id)),
+ entry.serial_number));
+ }
+ } else {
+ messages.push_back(l10n_util::GetStringFUTF16(
+ IDS_EXTENSION_PROMPT_WARNING_USB_DEVICE_VID_PID_SERIAL,
+ base::ASCIIToUTF16(base::StringPrintf("0x%04X", entry.vendor_id)),
+ base::ASCIIToUTF16(base::StringPrintf("0x%04X", entry.product_id)),
+ entry.serial_number));
+ }
+ }
+ return messages;
+}
+
+void DevicePermissionsManager::AllowUsbDevice(
+ const std::string& extension_id,
+ scoped_refptr<device::UsbDevice> device,
+ const base::string16& serial_number) {
+ DCHECK(CalledOnValidThread());
+ DevicePermissions* device_permissions = GetOrInsert(extension_id);
+
+ if (!serial_number.empty()) {
+ for (const auto& entry : device_permissions->permission_entries()) {
+ if (entry.vendor_id != device->vendor_id()) {
+ continue;
+ }
+ if (entry.product_id != device->product_id()) {
+ continue;
+ }
+ if (entry.serial_number == serial_number) {
+ return;
+ }
+ }
+
+ DevicePermissionEntry device_entry = DevicePermissionEntry(
+ device->vendor_id(), device->product_id(), serial_number);
+ device_permissions->permission_entries().push_back(device_entry);
+ SaveDevicePermissionEntry(context_, 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.
+ device_permissions->ephemeral_devices().insert(device);
+ device->AddObserver(this);
+ }
+}
+
+void DevicePermissionsManager::Clear(const std::string& extension_id) {
+ DCHECK(CalledOnValidThread());
+
+ ClearDevicePermissionEntries(ExtensionPrefs::Get(context_), extension_id);
+ std::map<std::string, DevicePermissions*>::iterator it =
+ extension_id_to_device_permissions_.find(extension_id);
+ if (it != extension_id_to_device_permissions_.end()) {
+ delete it->second;
+ extension_id_to_device_permissions_.erase(it);
+ }
+}
+
+DevicePermissionsManager::DevicePermissionsManager(
+ content::BrowserContext* context)
+ : context_(context) {
+ registrar_.Add(this,
+ extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED,
+ content::NotificationService::AllSources());
+}
+
+DevicePermissionsManager::~DevicePermissionsManager() {
+ for (const auto& map_entry : extension_id_to_device_permissions_) {
+ delete map_entry.second;
+ }
+}
+
+DevicePermissions* DevicePermissionsManager::Get(
+ const std::string& extension_id) const {
+ std::map<std::string, DevicePermissions*>::const_iterator it =
+ extension_id_to_device_permissions_.find(extension_id);
+ if (it != extension_id_to_device_permissions_.end()) {
+ return it->second;
+ }
+
+ return NULL;
+}
+
+DevicePermissions* DevicePermissionsManager::GetOrInsert(
+ const std::string& extension_id) {
+ DevicePermissions* device_permissions = Get(extension_id);
+ if (!device_permissions) {
+ device_permissions = new DevicePermissions(context_, extension_id);
+ extension_id_to_device_permissions_[extension_id] = device_permissions;
+ }
+
+ return device_permissions;
+}
+
+void DevicePermissionsManager::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK(CalledOnValidThread());
+ DCHECK_EQ(extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED, type);
+
+ ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
+ DevicePermissions* device_permissions = Get(host->extension_id());
+ if (device_permissions) {
+ // When the extension is unloaded all ephemeral device permissions are
+ // cleared.
+ for (std::set<scoped_refptr<UsbDevice>>::iterator it =
+ device_permissions->ephemeral_devices().begin();
+ it != device_permissions->ephemeral_devices().end();
+ ++it) {
+ (*it)->RemoveObserver(this);
+ }
+ device_permissions->ephemeral_devices().clear();
+ }
+}
+
+void DevicePermissionsManager::OnDisconnect(scoped_refptr<UsbDevice> device) {
+ for (const auto& map_entry : extension_id_to_device_permissions_) {
+ // An ephemeral device cannot be identified if it is reconnected and so
+ // permission to access it is cleared on disconnect.
+ map_entry.second->ephemeral_devices().erase(device);
+ device->RemoveObserver(this);
+ }
+}
+
+// static
+DevicePermissionsManager* DevicePermissionsManagerFactory::GetForBrowserContext(
+ content::BrowserContext* context) {
+ return static_cast<DevicePermissionsManager*>(
+ GetInstance()->GetServiceForBrowserContext(context, true));
+}
+
+// static
+DevicePermissionsManagerFactory*
+DevicePermissionsManagerFactory::GetInstance() {
+ return Singleton<DevicePermissionsManagerFactory>::get();
+}
+
+DevicePermissionsManagerFactory::DevicePermissionsManagerFactory()
+ : BrowserContextKeyedServiceFactory(
+ "DevicePermissionsManager",
+ BrowserContextDependencyManager::GetInstance()) {
+}
+
+DevicePermissionsManagerFactory::~DevicePermissionsManagerFactory() {
+}
+
+KeyedService* DevicePermissionsManagerFactory::BuildServiceInstanceFor(
+ content::BrowserContext* context) const {
+ return new DevicePermissionsManager(context);
+}
+
+} // namespace extensions
diff --git a/extensions/browser/api/device_permissions_manager.h b/extensions/browser/api/device_permissions_manager.h
new file mode 100644
index 0000000..9b79233
--- /dev/null
+++ b/extensions/browser/api/device_permissions_manager.h
@@ -0,0 +1,151 @@
+// 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 EXTENSIONS_DEVICE_PERMISSION_MANAGER_H_
+#define EXTENSIONS_DEVICE_PERMISSION_MANAGER_H_
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "base/threading/thread_checker.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.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"
+
+template <typename T>
+struct DefaultSingletonTraits;
+
+namespace base {
+class Value;
+}
+
+namespace content {
+class BrowserContext;
+}
+
+namespace device {
+class UsbDevice;
+}
+
+namespace extensions {
+
+// Stores information about a device saved with access granted.
+struct DevicePermissionEntry {
+ DevicePermissionEntry(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;
+};
+
+// Stores a copy of device permissions associated with a particular extension.
+class DevicePermissions {
+ public:
+ virtual ~DevicePermissions();
+
+ bool CheckUsbDevice(scoped_refptr<device::UsbDevice> device) const;
+
+ private:
+ friend class DevicePermissionsManager;
+
+ DevicePermissions(content::BrowserContext* context,
+ const std::string& extension_id);
+ DevicePermissions(
+ const std::vector<DevicePermissionEntry>& permission_entries,
+ const std::set<scoped_refptr<device::UsbDevice>>& ephemeral_devices);
+
+ std::vector<DevicePermissionEntry>& permission_entries();
+ std::set<scoped_refptr<device::UsbDevice>>& ephemeral_devices();
+
+ std::vector<DevicePermissionEntry> permission_entries_;
+ std::set<scoped_refptr<device::UsbDevice>> ephemeral_devices_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevicePermissions);
+};
+
+// Manages saved device permissions for all extensions.
+class DevicePermissionsManager : public KeyedService,
+ public base::NonThreadSafe,
+ public content::NotificationObserver,
+ public device::UsbDevice::Observer {
+ public:
+ static DevicePermissionsManager* Get(content::BrowserContext* context);
+
+ // Returns a copy of the DevicePermissions object for a given extension that
+ // can be used by any thread.
+ scoped_ptr<DevicePermissions> GetForExtension(
+ const std::string& extension_id);
+
+ std::vector<base::string16> GetPermissionMessageStrings(
+ const std::string& extension_id);
+
+ void AllowUsbDevice(const std::string& extension_id,
+ scoped_refptr<device::UsbDevice> device,
+ const base::string16& serial_number);
+
+ void Clear(const std::string& extension_id);
+
+ private:
+ friend class DevicePermissionsManagerFactory;
+
+ DevicePermissionsManager(content::BrowserContext* context);
+ virtual ~DevicePermissionsManager();
+
+ DevicePermissions* Get(const std::string& extension_id) const;
+ DevicePermissions* GetOrInsert(const std::string& extension_id);
+
+ // content::NotificationObserver.
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ // device::UsbDevice::Observer
+ virtual void OnDisconnect(scoped_refptr<device::UsbDevice> device) OVERRIDE;
+
+ content::BrowserContext* context_;
+ std::map<std::string, DevicePermissions*> extension_id_to_device_permissions_;
+ content::NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevicePermissionsManager);
+};
+
+class DevicePermissionsManagerFactory
+ : public BrowserContextKeyedServiceFactory {
+ public:
+ static DevicePermissionsManager* GetForBrowserContext(
+ content::BrowserContext* context);
+ static DevicePermissionsManagerFactory* GetInstance();
+
+ private:
+ friend struct DefaultSingletonTraits<DevicePermissionsManagerFactory>;
+
+ DevicePermissionsManagerFactory();
+ virtual ~DevicePermissionsManagerFactory();
+
+ // BrowserContextKeyedServiceFactory
+ virtual KeyedService* BuildServiceInstanceFor(
+ content::BrowserContext* context) const OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(DevicePermissionsManagerFactory);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_DEVICE_PERMISSION_MANAGER_H_
diff --git a/extensions/browser/api/usb/DEPS b/extensions/browser/api/usb/DEPS
index dce2c70..76a2fb6 100644
--- a/extensions/browser/api/usb/DEPS
+++ b/extensions/browser/api/usb/DEPS
@@ -1,4 +1,3 @@
include_rules = [
"+device/core",
- "+device/usb",
]
diff --git a/extensions/browser/api/usb_private/DEPS b/extensions/browser/api/usb_private/DEPS
index dce2c70..76a2fb6 100644
--- a/extensions/browser/api/usb_private/DEPS
+++ b/extensions/browser/api/usb_private/DEPS
@@ -1,4 +1,3 @@
include_rules = [
"+device/core",
- "+device/usb",
]
diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp
index 94cc3c4..e2ef5d0 100644
--- a/extensions/extensions.gyp
+++ b/extensions/extensions.gyp
@@ -394,6 +394,8 @@
'browser/api/declarative_webrequest/webrequest_constants.h',
'browser/api/declarative_webrequest/webrequest_rules_registry.cc',
'browser/api/declarative_webrequest/webrequest_rules_registry.h',
+ 'browser/api/device_permissions_manager.cc',
+ 'browser/api/device_permissions_manager.h',
'browser/api/dns/dns_api.cc',
'browser/api/dns/dns_api.h',
'browser/api/dns/host_resolver_wrapper.cc',
diff --git a/extensions/extensions_strings.grd b/extensions/extensions_strings.grd
index 7eb2f32..1553014 100644
--- a/extensions/extensions_strings.grd
+++ b/extensions/extensions_strings.grd
@@ -323,6 +323,15 @@
<message name="IDS_EXTENSION_TASK_MANAGER_WEBVIEW_TAG_PREFIX" desc="The prefix for a guest page loaded in a webview tag in the Task Manager">
Webview: <ph name="WEBVIEW_TAG_NAME">$1<ex>Google</ex></ph>
</message>
+ <message name="IDS_EXTENSION_PROMPT_WARNING_USB_DEVICE_SERIAL" desc="Permission string for accessing a USB device with a known vendor name, product name and serial number.">
+ <ph name="PRODUCT_NAME">$2<ex>Nexus 5</ex></ph> from <ph name="VENDOR_NAME">$1<ex>Google Inc.</ex></ph> (serial number <ph name="SERIAL_NUMBER">$3<ex>ABCDEF123456</ex></ph>)
+ </message>
+ <message name="IDS_EXTENSION_PROMPT_WARNING_USB_DEVICE_PID_SERIAL" desc="Permission string for accessing a USB device with a known vendor name and serial number and a hexidecimal product ID.">
+ Product <ph name="PRODUCT_ID">$2<ex>0x1234</ex></ph> from <ph name="VENDOR_NAME">$1<ex>Google Inc.</ex></ph> (serial number <ph name="SERIAL_NUMBER">$3<ex>ABCDEF123456</ex></ph>)
+ </message>
+ <message name="IDS_EXTENSION_PROMPT_WARNING_USB_DEVICE_VID_PID_SERIAL" desc="Permission string for accessing a USB device with a known serial number and a hexidecimal vendor and product IDs.">
+ Product <ph name="PRODUCT_ID">$2<ex>0x5678</ex></ph> from Vendor <ph name="VENDOR_ID">$1<ex>0x1234</ex></ph> (serial number <ph name="SERIAL_NUMBER">$3<ex>ABCDEF123456</ex></ph>)
+ </message>
<!-- Global error messages for extensions. Please keep alphabetized. -->
<message name="IDS_EXTENSION_WARNINGS_NETWORK_DELAY" desc="Warning message indicating that an extension caused excessive network delays for web requests">