Commit bc82d3c4 authored by reillyg's avatar reillyg Committed by Commit bot

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}
parent e01e6de4
...@@ -26,10 +26,6 @@ static_library("apps") { ...@@ -26,10 +26,6 @@ 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",
...@@ -45,7 +41,6 @@ static_library("apps") { ...@@ -45,7 +41,6 @@ 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,7 +9,6 @@ include_rules = [ ...@@ -9,7 +9,6 @@ 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,10 +45,6 @@ ...@@ -45,10 +45,6 @@
'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.
#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_
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "apps/saved_devices_service.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/test/values_test_util.h" #include "base/test/values_test_util.h"
...@@ -10,19 +9,18 @@ ...@@ -10,19 +9,18 @@
#include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile.h"
#include "device/usb/usb_device.h" #include "device/usb/usb_device.h"
#include "device/usb/usb_device_handle.h" #include "device/usb/usb_device_handle.h"
#include "extensions/browser/api/device_permissions_manager.h"
#include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_prefs.h"
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace apps { namespace extensions {
namespace { namespace {
using device::UsbDevice; using device::UsbDevice;
using device::UsbDeviceHandle; using device::UsbDeviceHandle;
using device::UsbEndpointDirection;
using device::UsbTransferCallback;
using testing::Return; using testing::Return;
class MockUsbDevice : public UsbDevice { class MockUsbDevice : public UsbDevice {
...@@ -39,12 +37,12 @@ class MockUsbDevice : public UsbDevice { ...@@ -39,12 +37,12 @@ class MockUsbDevice : public UsbDevice {
MOCK_METHOD1(GetManufacturer, bool(base::string16*)); MOCK_METHOD1(GetManufacturer, bool(base::string16*));
MOCK_METHOD1(GetProduct, 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()) { if (serial_number_.empty()) {
return false; return false;
} }
*serial = base::UTF8ToUTF16(serial_number_); *serial_number = base::UTF8ToUTF16(serial_number_);
return true; return true;
} }
...@@ -55,9 +53,10 @@ class MockUsbDevice : public UsbDevice { ...@@ -55,9 +53,10 @@ class MockUsbDevice : public UsbDevice {
const std::string serial_number_; const std::string serial_number_;
}; };
}
class SavedDevicesServiceTest : public testing::Test { } // namespace
class DevicePermissionsManagerTest : public testing::Test {
protected: protected:
virtual void SetUp() OVERRIDE { virtual void SetUp() OVERRIDE {
testing::Test::SetUp(); testing::Test::SetUp();
...@@ -73,7 +72,6 @@ class SavedDevicesServiceTest : public testing::Test { ...@@ -73,7 +72,6 @@ class SavedDevicesServiceTest : public testing::Test {
" \"usb\"" " \"usb\""
" ]" " ]"
"}")); "}"));
service_ = SavedDevicesService::Get(env_.profile());
device0 = new MockUsbDevice("ABCDE", 0); device0 = new MockUsbDevice("ABCDE", 0);
device1 = new MockUsbDevice("", 1); device1 = new MockUsbDevice("", 1);
device2 = new MockUsbDevice("12345", 2); device2 = new MockUsbDevice("12345", 2);
...@@ -82,53 +80,49 @@ class SavedDevicesServiceTest : public testing::Test { ...@@ -82,53 +80,49 @@ class SavedDevicesServiceTest : public testing::Test {
extensions::TestExtensionEnvironment env_; extensions::TestExtensionEnvironment env_;
const extensions::Extension* extension_; const extensions::Extension* extension_;
SavedDevicesService* service_;
scoped_refptr<MockUsbDevice> device0; scoped_refptr<MockUsbDevice> device0;
scoped_refptr<MockUsbDevice> device1; scoped_refptr<MockUsbDevice> device1;
scoped_refptr<MockUsbDevice> device2; scoped_refptr<MockUsbDevice> device2;
scoped_refptr<MockUsbDevice> device3; scoped_refptr<MockUsbDevice> device3;
}; };
TEST_F(SavedDevicesServiceTest, RegisterDevices) { TEST_F(DevicePermissionsManagerTest, RegisterDevices) {
SavedDevicesService::SavedDevices* saved_devices = DevicePermissionsManager* manager =
service_->GetOrInsert(extension_->id()); DevicePermissionsManager::Get(env_.profile());
manager->AllowUsbDevice(
base::string16 serial_number(base::ASCIIToUTF16("ABCDE")); extension_->id(), device0, base::ASCIIToUTF16("ABCDE"));
saved_devices->RegisterDevice(device0, &serial_number); manager->AllowUsbDevice(extension_->id(), device1, base::string16());
saved_devices->RegisterDevice(device1, NULL);
scoped_ptr<DevicePermissions> device_permissions =
// This is necessary as writing out registered devices happens in a task on manager->GetForExtension(extension_->id());
// the UI thread. ASSERT_TRUE(device_permissions->CheckUsbDevice(device0));
base::RunLoop run_loop; ASSERT_TRUE(device_permissions->CheckUsbDevice(device1));
run_loop.RunUntilIdle(); ASSERT_FALSE(device_permissions->CheckUsbDevice(device2));
ASSERT_FALSE(device_permissions->CheckUsbDevice(device3));
ASSERT_TRUE(saved_devices->IsRegistered(device0));
ASSERT_TRUE(saved_devices->IsRegistered(device1)); std::vector<base::string16> device_messages =
ASSERT_FALSE(saved_devices->IsRegistered(device2)); manager->GetPermissionMessageStrings(extension_->id());
ASSERT_FALSE(saved_devices->IsRegistered(device3)); ASSERT_EQ(1U, device_messages.size());
ASSERT_NE(device_messages[0].find(base::ASCIIToUTF16("ABCDE")),
std::vector<SavedDeviceEntry> device_entries = base::string16::npos);
service_->GetAllDevices(extension_->id());
ASSERT_EQ(1U, device_entries.size());
ASSERT_EQ(base::ASCIIToUTF16("ABCDE"), device_entries[0].serial_number);
device1->NotifyDisconnect(); device1->NotifyDisconnect();
ASSERT_TRUE(saved_devices->IsRegistered(device0)); device_permissions = manager->GetForExtension(extension_->id());
ASSERT_FALSE(saved_devices->IsRegistered(device1)); ASSERT_TRUE(device_permissions->CheckUsbDevice(device0));
ASSERT_FALSE(saved_devices->IsRegistered(device2)); ASSERT_FALSE(device_permissions->CheckUsbDevice(device1));
ASSERT_FALSE(saved_devices->IsRegistered(device3)); 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. device_permissions = manager->GetForExtension(extension_->id());
saved_devices = service_->GetOrInsert(extension_->id()); ASSERT_FALSE(device_permissions->CheckUsbDevice(device0));
ASSERT_FALSE(saved_devices->IsRegistered(device0)); device_messages = manager->GetPermissionMessageStrings(extension_->id());
device_entries = service_->GetAllDevices(extension_->id()); ASSERT_EQ(0U, device_messages.size());
ASSERT_EQ(0U, device_entries.size());
} }
TEST_F(SavedDevicesServiceTest, LoadPrefs) { TEST_F(DevicePermissionsManagerTest, LoadPrefs) {
scoped_ptr<base::Value> prefs_value = base::test::ParseJson( scoped_ptr<base::Value> prefs_value = base::test::ParseJson(
"[" "["
" {" " {"
...@@ -141,12 +135,14 @@ TEST_F(SavedDevicesServiceTest, LoadPrefs) { ...@@ -141,12 +135,14 @@ TEST_F(SavedDevicesServiceTest, LoadPrefs) {
env_.GetExtensionPrefs()->UpdateExtensionPref( env_.GetExtensionPrefs()->UpdateExtensionPref(
extension_->id(), "devices", prefs_value.release()); extension_->id(), "devices", prefs_value.release());
SavedDevicesService::SavedDevices* saved_devices = DevicePermissionsManager* manager =
service_->GetOrInsert(extension_->id()); DevicePermissionsManager::Get(env_.profile());
ASSERT_TRUE(saved_devices->IsRegistered(device0)); scoped_ptr<DevicePermissions> device_permissions =
ASSERT_FALSE(saved_devices->IsRegistered(device1)); manager->GetForExtension(extension_->id());
ASSERT_FALSE(saved_devices->IsRegistered(device2)); ASSERT_TRUE(device_permissions->CheckUsbDevice(device0));
ASSERT_FALSE(saved_devices->IsRegistered(device3)); ASSERT_FALSE(device_permissions->CheckUsbDevice(device1));
ASSERT_FALSE(device_permissions->CheckUsbDevice(device2));
ASSERT_FALSE(device_permissions->CheckUsbDevice(device3));
} }
} // namespace apps } // namespace extensions
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
{ {
'variables': { 'variables': {
'chrome_unit_tests_sources': [ 'chrome_unit_tests_sources': [
'../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',
...@@ -317,6 +316,7 @@ ...@@ -317,6 +316,7 @@
'browser/extensions/api/declarative_content/chrome_content_rules_registry_unittest.cc', '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_action_unittest.cc',
'browser/extensions/api/declarative_webrequest/webrequest_rules_registry_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_device_data_unittest.cc',
'browser/extensions/api/dial/dial_registry_unittest.cc', 'browser/extensions/api/dial/dial_registry_unittest.cc',
'browser/extensions/api/dial/dial_service_unittest.cc', 'browser/extensions/api/dial/dial_service_unittest.cc',
......
...@@ -122,6 +122,8 @@ source_set("browser") { ...@@ -122,6 +122,8 @@ source_set("browser") {
"api/declarative_webrequest/webrequest_constants.h", "api/declarative_webrequest/webrequest_constants.h",
"api/declarative_webrequest/webrequest_rules_registry.cc", "api/declarative_webrequest/webrequest_rules_registry.cc",
"api/declarative_webrequest/webrequest_rules_registry.h", "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.cc",
"api/dns/dns_api.h", "api/dns/dns_api.h",
"api/dns/host_resolver_wrapper.cc", "api/dns/host_resolver_wrapper.cc",
......
...@@ -7,6 +7,7 @@ include_rules = [ ...@@ -7,6 +7,7 @@ include_rules = [
"+components/web_modal", "+components/web_modal",
"+content/public/browser", "+content/public/browser",
"+device/bluetooth", "+device/bluetooth",
"+device/usb",
"+grit/extensions_strings.h", "+grit/extensions_strings.h",
"+net", "+net",
"+skia/ext/image_operations.h", "+skia/ext/image_operations.h",
......
...@@ -2,33 +2,25 @@ ...@@ -2,33 +2,25 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "apps/saved_devices_service.h" #include "extensions/browser/api/device_permissions_manager.h"
#include <set> #include "base/memory/singleton.h"
#include <vector> #include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "apps/saved_devices_service_factory.h"
#include "base/basictypes.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/browser/chrome_notification_types.h" #include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h" #include "content/public/browser/notification_service.h"
#include "device/usb/usb_device.h" #include "device/usb/usb_ids.h"
#include "device/usb/usb_device_handle.h"
#include "extensions/browser/extension_host.h" #include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_prefs.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/browser/notification_types.h"
#include "extensions/common/permissions/api_permission.h" #include "extensions/strings/grit/extensions_strings.h"
#include "extensions/common/permissions/permission_set.h" #include "ui/base/l10n/l10n_util.h"
#include "extensions/common/permissions/permissions_data.h"
namespace apps { namespace extensions {
using content::BrowserContext;
using device::UsbDevice; using device::UsbDevice;
using device::UsbDeviceHandle;
using extensions::APIPermission; using extensions::APIPermission;
using extensions::Extension; using extensions::Extension;
using extensions::ExtensionHost; using extensions::ExtensionHost;
...@@ -56,11 +48,11 @@ const char kDeviceProductId[] = "product_id"; ...@@ -56,11 +48,11 @@ const char kDeviceProductId[] = "product_id";
// The serial number of the device that the app has permission to access. // The serial number of the device that the app has permission to access.
const char kDeviceSerialNumber[] = "serial_number"; const char kDeviceSerialNumber[] = "serial_number";
// Persists a SavedDeviceEntry in ExtensionPrefs. // Persists a DevicePermissionEntry in ExtensionPrefs.
void AddSavedDeviceEntry(Profile* profile, void SaveDevicePermissionEntry(BrowserContext* context,
const std::string& extension_id, const std::string& extension_id,
const SavedDeviceEntry& device) { const DevicePermissionEntry& device) {
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile); ExtensionPrefs* prefs = ExtensionPrefs::Get(context);
ExtensionPrefs::ScopedListUpdate update(prefs, extension_id, kDevices); ExtensionPrefs::ScopedListUpdate update(prefs, extension_id, kDevices);
base::ListValue* devices = update.Get(); base::ListValue* devices = update.Get();
if (!devices) { if (!devices) {
...@@ -72,17 +64,17 @@ void AddSavedDeviceEntry(Profile* profile, ...@@ -72,17 +64,17 @@ void AddSavedDeviceEntry(Profile* profile,
devices->Append(device_entry); devices->Append(device_entry);
} }
// Clears all SavedDeviceEntry for the app from ExtensionPrefs. // Clears all DevicePermissionEntries for the app from ExtensionPrefs.
void ClearSavedDeviceEntries(ExtensionPrefs* prefs, void ClearDevicePermissionEntries(ExtensionPrefs* prefs,
const std::string& extension_id) { const std::string& extension_id) {
prefs->UpdateExtensionPref(extension_id, kDevices, NULL); prefs->UpdateExtensionPref(extension_id, kDevices, NULL);
} }
// Returns all SavedDeviceEntries for the app. // Returns all DevicePermissionEntries for the app.
std::vector<SavedDeviceEntry> GetSavedDeviceEntries( std::vector<DevicePermissionEntry> GetDevicePermissionEntries(
ExtensionPrefs* prefs, ExtensionPrefs* prefs,
const std::string& extension_id) { const std::string& extension_id) {
std::vector<SavedDeviceEntry> result; std::vector<DevicePermissionEntry> result;
const base::ListValue* devices = NULL; const base::ListValue* devices = NULL;
if (!prefs->ReadPrefAsList(extension_id, kDevices, &devices)) { if (!prefs->ReadPrefAsList(extension_id, kDevices, &devices)) {
return result; return result;
...@@ -113,22 +105,23 @@ std::vector<SavedDeviceEntry> GetSavedDeviceEntries( ...@@ -113,22 +105,23 @@ std::vector<SavedDeviceEntry> GetSavedDeviceEntries(
continue; continue;
} }
result.push_back(SavedDeviceEntry(vendor_id, product_id, serial_number)); result.push_back(
DevicePermissionEntry(vendor_id, product_id, serial_number));
} }
return result; return result;
} }
}
} // namespace DevicePermissionEntry::DevicePermissionEntry(
uint16_t vendor_id,
SavedDeviceEntry::SavedDeviceEntry(uint16_t vendor_id, uint16_t product_id,
uint16_t product_id, const base::string16& serial_number)
const base::string16& serial_number)
: vendor_id(vendor_id), : vendor_id(vendor_id),
product_id(product_id), product_id(product_id),
serial_number(serial_number) { serial_number(serial_number) {
} }
base::Value* SavedDeviceEntry::ToValue() const { base::Value* DevicePermissionEntry::ToValue() const {
base::DictionaryValue* device_entry_dict = new base::DictionaryValue(); base::DictionaryValue* device_entry_dict = new base::DictionaryValue();
device_entry_dict->SetStringWithoutPathExpansion(kDeviceType, kDeviceTypeUsb); device_entry_dict->SetStringWithoutPathExpansion(kDeviceType, kDeviceTypeUsb);
device_entry_dict->SetIntegerWithoutPathExpansion(kDeviceVendorId, vendor_id); device_entry_dict->SetIntegerWithoutPathExpansion(kDeviceVendorId, vendor_id);
...@@ -139,22 +132,22 @@ base::Value* SavedDeviceEntry::ToValue() const { ...@@ -139,22 +132,22 @@ base::Value* SavedDeviceEntry::ToValue() const {
return device_entry_dict; return device_entry_dict;
} }
bool SavedDevicesService::SavedDevices::IsRegistered( DevicePermissions::~DevicePermissions() {
scoped_refptr<UsbDevice> device) const { }
bool DevicePermissions::CheckUsbDevice(
scoped_refptr<device::UsbDevice> device) const {
if (ephemeral_devices_.find(device) != ephemeral_devices_.end()) { if (ephemeral_devices_.find(device) != ephemeral_devices_.end()) {
return true; return true;
} }
bool have_serial_number = false; bool have_serial_number = false;
base::string16 serial_number; base::string16 serial_number;
for (std::vector<SavedDeviceEntry>::const_iterator it = for (const auto& entry : permission_entries_) {
persistent_devices_.begin(); if (entry.vendor_id != device->vendor_id()) {
it != persistent_devices_.end();
++it) {
if (it->vendor_id != device->vendor_id()) {
continue; continue;
} }
if (it->product_id != device->product_id()) { if (entry.product_id != device->product_id()) {
continue; continue;
} }
if (!have_serial_number) { if (!have_serial_number) {
...@@ -163,7 +156,7 @@ bool SavedDevicesService::SavedDevices::IsRegistered( ...@@ -163,7 +156,7 @@ bool SavedDevicesService::SavedDevices::IsRegistered(
} }
have_serial_number = true; have_serial_number = true;
} }
if (it->serial_number != serial_number) { if (entry.serial_number != serial_number) {
continue; continue;
} }
return true; return true;
...@@ -171,146 +164,221 @@ bool SavedDevicesService::SavedDevices::IsRegistered( ...@@ -171,146 +164,221 @@ bool SavedDevicesService::SavedDevices::IsRegistered(
return false; return false;
} }
void SavedDevicesService::SavedDevices::RegisterDevice( 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, scoped_refptr<device::UsbDevice> device,
base::string16* serial_number) { const base::string16& serial_number) {
if (serial_number) { DCHECK(CalledOnValidThread());
for (std::vector<SavedDeviceEntry>::const_iterator it = DevicePermissions* device_permissions = GetOrInsert(extension_id);
persistent_devices_.begin();
it != persistent_devices_.end(); if (!serial_number.empty()) {
++it) { for (const auto& entry : device_permissions->permission_entries()) {
if (it->vendor_id != device->vendor_id()) { if (entry.vendor_id != device->vendor_id()) {
continue; continue;
} }
if (it->product_id != device->product_id()) { if (entry.product_id != device->product_id()) {
continue; continue;
} }
if (it->serial_number == *serial_number) { if (entry.serial_number == serial_number) {
return; return;
} }
} }
SavedDeviceEntry device_entry = SavedDeviceEntry(
device->vendor_id(), device->product_id(), *serial_number); DevicePermissionEntry device_entry = DevicePermissionEntry(
persistent_devices_.push_back(device_entry); device->vendor_id(), device->product_id(), serial_number);
device_permissions->permission_entries().push_back(device_entry);
content::BrowserThread::PostTask( SaveDevicePermissionEntry(context_, extension_id, device_entry);
content::BrowserThread::UI,
FROM_HERE,
base::Bind(AddSavedDeviceEntry, profile_, extension_id_, device_entry));
} else { } else {
// Without a serial number a device cannot be reliably identified when it // Without a serial number a device cannot be reliably identified when it
// is reconnected so such devices are only remembered until disconnect. // is reconnected so such devices are only remembered until disconnect.
// Register an observer here so that this set doesn't grow undefinitely. // Register an observer here so that this set doesn't grow undefinitely.
ephemeral_devices_.insert(device); device_permissions->ephemeral_devices().insert(device);
device->AddObserver(this); device->AddObserver(this);
} }
} }
SavedDevicesService::SavedDevices::SavedDevices(Profile* profile, void DevicePermissionsManager::Clear(const std::string& extension_id) {
const std::string& extension_id) DCHECK(CalledOnValidThread());
: profile_(profile), extension_id_(extension_id) {
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
persistent_devices_ = GetSavedDeviceEntries(prefs, extension_id_);
}
SavedDevicesService::SavedDevices::~SavedDevices() { ClearDevicePermissionEntries(ExtensionPrefs::Get(context_), extension_id);
// Only ephemeral devices have an observer registered. std::map<std::string, DevicePermissions*>::iterator it =
for (std::set<scoped_refptr<UsbDevice> >::iterator it = extension_id_to_device_permissions_.find(extension_id);
ephemeral_devices_.begin(); if (it != extension_id_to_device_permissions_.end()) {
it != ephemeral_devices_.end(); delete it->second;
++it) { extension_id_to_device_permissions_.erase(it);
(*it)->RemoveObserver(this);
} }
} }
void SavedDevicesService::SavedDevices::OnDisconnect( DevicePermissionsManager::DevicePermissionsManager(
scoped_refptr<UsbDevice> device) { content::BrowserContext* context)
// Permission for an ephemeral device lasts only as long as the device is : context_(context) {
// plugged in.
ephemeral_devices_.erase(device);
device->RemoveObserver(this);
}
SavedDevicesService::SavedDevicesService(Profile* profile) : profile_(profile) {
registrar_.Add(this, registrar_.Add(this,
extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED, extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED,
content::NotificationService::AllSources()); content::NotificationService::AllSources());
registrar_.Add(this,
chrome::NOTIFICATION_APP_TERMINATING,
content::NotificationService::AllSources());
} }
SavedDevicesService::~SavedDevicesService() { DevicePermissionsManager::~DevicePermissionsManager() {
for (std::map<std::string, SavedDevices*>::iterator it = for (const auto& map_entry : extension_id_to_device_permissions_) {
extension_id_to_saved_devices_.begin(); delete map_entry.second;
it != extension_id_to_saved_devices_.end();
++it) {
delete it->second;
} }
} }
// static DevicePermissions* DevicePermissionsManager::Get(
SavedDevicesService* SavedDevicesService::Get(Profile* profile) { const std::string& extension_id) const {
return SavedDevicesServiceFactory::GetForProfile(profile); 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;
} }
SavedDevicesService::SavedDevices* SavedDevicesService::GetOrInsert( DevicePermissions* DevicePermissionsManager::GetOrInsert(
const std::string& extension_id) { const std::string& extension_id) {
SavedDevices* saved_devices = Get(extension_id); DevicePermissions* device_permissions = Get(extension_id);
if (saved_devices) { if (!device_permissions) {
return saved_devices; device_permissions = new DevicePermissions(context_, extension_id);
extension_id_to_device_permissions_[extension_id] = device_permissions;
} }
saved_devices = new SavedDevices(profile_, extension_id); return device_permissions;
extension_id_to_saved_devices_[extension_id] = saved_devices;
return saved_devices;
} }
std::vector<SavedDeviceEntry> SavedDevicesService::GetAllDevices( void DevicePermissionsManager::Observe(
const std::string& extension_id) const { int type,
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_); const content::NotificationSource& source,
return GetSavedDeviceEntries(prefs, extension_id); 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 SavedDevicesService::Clear(const std::string& extension_id) { void DevicePermissionsManager::OnDisconnect(scoped_refptr<UsbDevice> device) {
DCHECK(thread_checker_.CalledOnValidThread()); for (const auto& map_entry : extension_id_to_device_permissions_) {
ClearSavedDeviceEntries(ExtensionPrefs::Get(profile_), extension_id); // An ephemeral device cannot be identified if it is reconnected and so
std::map<std::string, SavedDevices*>::iterator it = // permission to access it is cleared on disconnect.
extension_id_to_saved_devices_.find(extension_id); map_entry.second->ephemeral_devices().erase(device);
if (it != extension_id_to_saved_devices_.end()) { device->RemoveObserver(this);
delete it->second;
extension_id_to_saved_devices_.erase(it);
} }
} }
void SavedDevicesService::Observe(int type, // static
const content::NotificationSource& source, DevicePermissionsManager* DevicePermissionsManagerFactory::GetForBrowserContext(
const content::NotificationDetails& details) { content::BrowserContext* context) {
switch (type) { return static_cast<DevicePermissionsManager*>(
case extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED: { GetInstance()->GetServiceForBrowserContext(context, true));
Clear(content::Details<ExtensionHost>(details)->extension_id()); }
break;
}
case chrome::NOTIFICATION_APP_TERMINATING: { // static
// Stop listening to NOTIFICATION_EXTENSION_HOST_DESTROYED in particular DevicePermissionsManagerFactory*
// as all extension hosts will be destroyed as a result of shutdown. DevicePermissionsManagerFactory::GetInstance() {
registrar_.RemoveAll(); return Singleton<DevicePermissionsManagerFactory>::get();
break;
}
}
} }
SavedDevicesService::SavedDevices* SavedDevicesService::Get( DevicePermissionsManagerFactory::DevicePermissionsManagerFactory()
const std::string& extension_id) const { : BrowserContextKeyedServiceFactory(
DCHECK(thread_checker_.CalledOnValidThread()); "DevicePermissionsManager",
std::map<std::string, SavedDevices*>::const_iterator it = BrowserContextDependencyManager::GetInstance()) {
extension_id_to_saved_devices_.find(extension_id); }
if (it != extension_id_to_saved_devices_.end()) {
return it->second;
}
return NULL; DevicePermissionsManagerFactory::~DevicePermissionsManagerFactory() {
}
KeyedService* DevicePermissionsManagerFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
return new DevicePermissionsManager(context);
} }
} // namespace apps } // namespace extensions
// 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_
include_rules = [ include_rules = [
"+device/core", "+device/core",
"+device/usb",
] ]
include_rules = [ include_rules = [
"+device/core", "+device/core",
"+device/usb",
] ]
...@@ -394,6 +394,8 @@ ...@@ -394,6 +394,8 @@
'browser/api/declarative_webrequest/webrequest_constants.h', 'browser/api/declarative_webrequest/webrequest_constants.h',
'browser/api/declarative_webrequest/webrequest_rules_registry.cc', 'browser/api/declarative_webrequest/webrequest_rules_registry.cc',
'browser/api/declarative_webrequest/webrequest_rules_registry.h', '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.cc',
'browser/api/dns/dns_api.h', 'browser/api/dns/dns_api.h',
'browser/api/dns/host_resolver_wrapper.cc', 'browser/api/dns/host_resolver_wrapper.cc',
......
...@@ -323,6 +323,15 @@ ...@@ -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"> <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> Webview: <ph name="WEBVIEW_TAG_NAME">$1<ex>Google</ex></ph>
</message> </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. --> <!-- 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"> <message name="IDS_EXTENSION_WARNINGS_NETWORK_DELAY" desc="Warning message indicating that an extension caused excessive network delays for web requests">
......
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