blob: 96a940f0cc812d7f05afd2f495570f1c86a408b1 [file] [log] [blame]
// 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_prompt.h"
#include <utility>
#include "base/bind.h"
#include "base/i18n/message_formatter.h"
#include "base/scoped_observer.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "device/core/device_client.h"
#include "device/hid/hid_device_filter.h"
#include "device/hid/hid_device_info.h"
#include "device/hid/hid_service.h"
#include "device/usb/usb_device.h"
#include "device/usb/usb_device_filter.h"
#include "device/usb/usb_ids.h"
#include "device/usb/usb_service.h"
#include "extensions/browser/api/device_permissions_manager.h"
#include "extensions/common/extension.h"
#include "extensions/strings/grit/extensions_strings.h"
#include "ui/base/l10n/l10n_util.h"
#if defined(OS_CHROMEOS)
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/permission_broker_client.h"
#include "device/hid/hid_device_info_linux.h"
#endif // defined(OS_CHROMEOS)
using device::HidDeviceFilter;
using device::HidService;
using device::UsbDevice;
using device::UsbDeviceFilter;
using device::UsbService;
namespace extensions {
namespace {
void NoopHidCallback(const std::vector<scoped_refptr<device::HidDeviceInfo>>&) {
}
void NoopUsbCallback(const std::vector<scoped_refptr<device::UsbDevice>>&) {}
class UsbDeviceInfo : public DevicePermissionsPrompt::Prompt::DeviceInfo {
public:
UsbDeviceInfo(scoped_refptr<UsbDevice> device) : device_(device) {
name_ = DevicePermissionsManager::GetPermissionMessage(
device->vendor_id(), device->product_id(),
device->manufacturer_string(), device->product_string(),
base::string16(), // Serial number is displayed separately.
true);
serial_number_ = device->serial_number();
}
~UsbDeviceInfo() override {}
const scoped_refptr<UsbDevice>& device() const { return device_; }
private:
// TODO(reillyg): Convert this to a weak reference when UsbDevice has a
// connected flag.
scoped_refptr<UsbDevice> device_;
};
class UsbDevicePermissionsPrompt : public DevicePermissionsPrompt::Prompt,
public device::UsbService::Observer {
public:
UsbDevicePermissionsPrompt(
const Extension* extension,
content::BrowserContext* context,
bool multiple,
const std::vector<UsbDeviceFilter>& filters,
const DevicePermissionsPrompt::UsbDevicesCallback& callback)
: Prompt(extension, context, multiple),
filters_(filters),
callback_(callback),
service_observer_(this) {}
private:
~UsbDevicePermissionsPrompt() override {}
// DevicePermissionsPrompt::Prompt implementation:
void SetObserver(
DevicePermissionsPrompt::Prompt::Observer* observer) override {
DevicePermissionsPrompt::Prompt::SetObserver(observer);
if (observer) {
UsbService* service = device::DeviceClient::Get()->GetUsbService();
if (service && !service_observer_.IsObserving(service)) {
service->GetDevices(
base::Bind(&UsbDevicePermissionsPrompt::OnDevicesEnumerated, this));
service_observer_.Add(service);
}
}
}
base::string16 GetHeading() const override {
return l10n_util::GetSingleOrMultipleStringUTF16(
IDS_USB_DEVICE_PERMISSIONS_PROMPT_TITLE, multiple());
}
void Dismissed() override {
DevicePermissionsManager* permissions_manager =
DevicePermissionsManager::Get(browser_context());
std::vector<scoped_refptr<UsbDevice>> devices;
for (const auto& device : devices_) {
if (device->granted()) {
const UsbDeviceInfo* usb_device =
static_cast<const UsbDeviceInfo*>(device.get());
devices.push_back(usb_device->device());
if (permissions_manager) {
permissions_manager->AllowUsbDevice(extension()->id(),
usb_device->device());
}
}
}
DCHECK(multiple() || devices.size() <= 1);
callback_.Run(devices);
callback_.Reset();
}
// device::UsbService::Observer implementation:
void OnDeviceAdded(scoped_refptr<UsbDevice> device) override {
if (!UsbDeviceFilter::MatchesAny(device, filters_))
return;
std::unique_ptr<DeviceInfo> device_info(new UsbDeviceInfo(device));
device->CheckUsbAccess(
base::Bind(&UsbDevicePermissionsPrompt::AddCheckedDevice, this,
base::Passed(&device_info)));
}
void OnDeviceRemoved(scoped_refptr<UsbDevice> device) override {
for (auto it = devices_.begin(); it != devices_.end(); ++it) {
const UsbDeviceInfo* entry =
static_cast<const UsbDeviceInfo*>((*it).get());
if (entry->device() == device) {
devices_.erase(it);
if (observer()) {
observer()->OnDevicesChanged();
}
return;
}
}
}
void OnDevicesEnumerated(
const std::vector<scoped_refptr<UsbDevice>>& devices) {
for (const auto& device : devices) {
OnDeviceAdded(device);
}
}
std::vector<UsbDeviceFilter> filters_;
DevicePermissionsPrompt::UsbDevicesCallback callback_;
ScopedObserver<UsbService, UsbService::Observer> service_observer_;
};
class HidDeviceInfo : public DevicePermissionsPrompt::Prompt::DeviceInfo {
public:
HidDeviceInfo(scoped_refptr<device::HidDeviceInfo> device) : device_(device) {
name_ = DevicePermissionsManager::GetPermissionMessage(
device->vendor_id(), device->product_id(),
base::string16(), // HID devices include manufacturer in product name.
base::UTF8ToUTF16(device->product_name()),
base::string16(), // Serial number is displayed separately.
false);
serial_number_ = base::UTF8ToUTF16(device->serial_number());
}
~HidDeviceInfo() override {}
const scoped_refptr<device::HidDeviceInfo>& device() const { return device_; }
private:
scoped_refptr<device::HidDeviceInfo> device_;
};
class HidDevicePermissionsPrompt : public DevicePermissionsPrompt::Prompt,
public device::HidService::Observer {
public:
HidDevicePermissionsPrompt(
const Extension* extension,
content::BrowserContext* context,
bool multiple,
const std::vector<HidDeviceFilter>& filters,
const DevicePermissionsPrompt::HidDevicesCallback& callback)
: Prompt(extension, context, multiple),
filters_(filters),
callback_(callback),
service_observer_(this) {}
private:
~HidDevicePermissionsPrompt() override {}
// DevicePermissionsPrompt::Prompt implementation:
void SetObserver(
DevicePermissionsPrompt::Prompt::Observer* observer) override {
DevicePermissionsPrompt::Prompt::SetObserver(observer);
if (observer) {
HidService* service = device::DeviceClient::Get()->GetHidService();
if (service && !service_observer_.IsObserving(service)) {
service->GetDevices(
base::Bind(&HidDevicePermissionsPrompt::OnDevicesEnumerated, this));
service_observer_.Add(service);
}
}
}
base::string16 GetHeading() const override {
return l10n_util::GetSingleOrMultipleStringUTF16(
IDS_HID_DEVICE_PERMISSIONS_PROMPT_TITLE, multiple());
}
void Dismissed() override {
DevicePermissionsManager* permissions_manager =
DevicePermissionsManager::Get(browser_context());
std::vector<scoped_refptr<device::HidDeviceInfo>> devices;
for (const auto& device : devices_) {
if (device->granted()) {
const HidDeviceInfo* hid_device =
static_cast<const HidDeviceInfo*>(device.get());
devices.push_back(hid_device->device());
if (permissions_manager) {
permissions_manager->AllowHidDevice(extension()->id(),
hid_device->device());
}
}
}
DCHECK(multiple() || devices.size() <= 1);
callback_.Run(devices);
callback_.Reset();
}
// device::HidService::Observer implementation:
void OnDeviceAdded(scoped_refptr<device::HidDeviceInfo> device) override {
if (HasUnprotectedCollections(device) &&
(filters_.empty() || HidDeviceFilter::MatchesAny(device, filters_))) {
std::unique_ptr<DeviceInfo> device_info(new HidDeviceInfo(device));
#if defined(OS_CHROMEOS)
chromeos::PermissionBrokerClient* client =
chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
DCHECK(client) << "Could not get permission broker client.";
device::HidDeviceInfoLinux* linux_device_info =
static_cast<device::HidDeviceInfoLinux*>(device.get());
client->CheckPathAccess(
linux_device_info->device_node(),
base::Bind(&HidDevicePermissionsPrompt::AddCheckedDevice, this,
base::Passed(&device_info)));
#else
AddCheckedDevice(std::move(device_info), true);
#endif // defined(OS_CHROMEOS)
}
}
void OnDeviceRemoved(scoped_refptr<device::HidDeviceInfo> device) override {
for (auto it = devices_.begin(); it != devices_.end(); ++it) {
const HidDeviceInfo* entry =
static_cast<const HidDeviceInfo*>((*it).get());
if (entry->device() == device) {
devices_.erase(it);
if (observer()) {
observer()->OnDevicesChanged();
}
return;
}
}
}
void OnDevicesEnumerated(
const std::vector<scoped_refptr<device::HidDeviceInfo>>& devices) {
for (const auto& device : devices) {
OnDeviceAdded(device);
}
}
bool HasUnprotectedCollections(scoped_refptr<device::HidDeviceInfo> device) {
for (const auto& collection : device->collections()) {
if (!collection.usage.IsProtected()) {
return true;
}
}
return false;
}
std::vector<HidDeviceFilter> filters_;
DevicePermissionsPrompt::HidDevicesCallback callback_;
ScopedObserver<HidService, HidService::Observer> service_observer_;
};
} // namespace
DevicePermissionsPrompt::Prompt::DeviceInfo::DeviceInfo() {
}
DevicePermissionsPrompt::Prompt::DeviceInfo::~DeviceInfo() {
}
DevicePermissionsPrompt::Prompt::Observer::~Observer() {
}
DevicePermissionsPrompt::Prompt::Prompt(const Extension* extension,
content::BrowserContext* context,
bool multiple)
: extension_(extension), browser_context_(context), multiple_(multiple) {
}
void DevicePermissionsPrompt::Prompt::SetObserver(Observer* observer) {
observer_ = observer;
}
base::string16 DevicePermissionsPrompt::Prompt::GetPromptMessage() const {
return base::i18n::MessageFormatter::FormatWithNumberedArgs(
l10n_util::GetStringUTF16(IDS_DEVICE_PERMISSIONS_PROMPT),
multiple_ ? "multiple" : "single", extension_->name());
}
base::string16 DevicePermissionsPrompt::Prompt::GetDeviceName(
size_t index) const {
DCHECK_LT(index, devices_.size());
return devices_[index]->name();
}
base::string16 DevicePermissionsPrompt::Prompt::GetDeviceSerialNumber(
size_t index) const {
DCHECK_LT(index, devices_.size());
return devices_[index]->serial_number();
}
void DevicePermissionsPrompt::Prompt::GrantDevicePermission(size_t index) {
DCHECK_LT(index, devices_.size());
devices_[index]->set_granted();
}
DevicePermissionsPrompt::Prompt::~Prompt() {
}
void DevicePermissionsPrompt::Prompt::AddCheckedDevice(
std::unique_ptr<DeviceInfo> device,
bool allowed) {
if (allowed) {
devices_.push_back(std::move(device));
if (observer_) {
observer_->OnDevicesChanged();
}
}
}
DevicePermissionsPrompt::DevicePermissionsPrompt(
content::WebContents* web_contents)
: web_contents_(web_contents) {
}
DevicePermissionsPrompt::~DevicePermissionsPrompt() {
}
void DevicePermissionsPrompt::AskForUsbDevices(
const Extension* extension,
content::BrowserContext* context,
bool multiple,
const std::vector<UsbDeviceFilter>& filters,
const UsbDevicesCallback& callback) {
prompt_ = new UsbDevicePermissionsPrompt(extension, context, multiple,
filters, callback);
ShowDialog();
}
void DevicePermissionsPrompt::AskForHidDevices(
const Extension* extension,
content::BrowserContext* context,
bool multiple,
const std::vector<HidDeviceFilter>& filters,
const HidDevicesCallback& callback) {
prompt_ = new HidDevicePermissionsPrompt(extension, context, multiple,
filters, callback);
ShowDialog();
}
// static
scoped_refptr<DevicePermissionsPrompt::Prompt>
DevicePermissionsPrompt::CreateHidPromptForTest(const Extension* extension,
bool multiple) {
return make_scoped_refptr(new HidDevicePermissionsPrompt(
extension, nullptr, multiple, std::vector<HidDeviceFilter>(),
base::Bind(&NoopHidCallback)));
}
// static
scoped_refptr<DevicePermissionsPrompt::Prompt>
DevicePermissionsPrompt::CreateUsbPromptForTest(const Extension* extension,
bool multiple) {
return make_scoped_refptr(new UsbDevicePermissionsPrompt(
extension, nullptr, multiple, std::vector<UsbDeviceFilter>(),
base::Bind(&NoopUsbCallback)));
}
} // namespace extensions