1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "extensions/browser/api/device_permissions_prompt.h"
8 #include "base/i18n/message_formatter.h"
9 #include "base/scoped_observer.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "device/core/device_client.h"
13 #include "device/hid/hid_device_filter.h"
14 #include "device/hid/hid_device_info.h"
15 #include "device/hid/hid_service.h"
16 #include "device/usb/usb_device.h"
17 #include "device/usb/usb_device_filter.h"
18 #include "device/usb/usb_ids.h"
19 #include "device/usb/usb_service.h"
20 #include "extensions/browser/api/device_permissions_manager.h"
21 #include "extensions/common/extension.h"
22 #include "extensions/strings/grit/extensions_strings.h"
23 #include "ui/base/l10n/l10n_util.h"
25 #if defined(OS_CHROMEOS)
26 #include "chromeos/dbus/dbus_thread_manager.h"
27 #include "chromeos/dbus/permission_broker_client.h"
28 #include "device/hid/hid_device_info_linux.h"
29 #endif // defined(OS_CHROMEOS)
31 using device::HidDeviceFilter
;
32 using device::HidService
;
33 using device::UsbDevice
;
34 using device::UsbDeviceFilter
;
35 using device::UsbService
;
37 namespace extensions
{
41 void NoopHidCallback(const std::vector
<scoped_refptr
<device::HidDeviceInfo
>>&) {
44 void NoopUsbCallback(const std::vector
<scoped_refptr
<device::UsbDevice
>>&) {}
46 class UsbDeviceInfo
: public DevicePermissionsPrompt::Prompt::DeviceInfo
{
48 UsbDeviceInfo(scoped_refptr
<UsbDevice
> device
) : device_(device
) {
49 name_
= DevicePermissionsManager::GetPermissionMessage(
50 device
->vendor_id(), device
->product_id(),
51 device
->manufacturer_string(), device
->product_string(),
52 base::string16(), // Serial number is displayed separately.
54 serial_number_
= device
->serial_number();
57 ~UsbDeviceInfo() override
{}
59 const scoped_refptr
<UsbDevice
>& device() const { return device_
; }
62 // TODO(reillyg): Convert this to a weak reference when UsbDevice has a
64 scoped_refptr
<UsbDevice
> device_
;
67 class UsbDevicePermissionsPrompt
: public DevicePermissionsPrompt::Prompt
,
68 public device::UsbService::Observer
{
70 UsbDevicePermissionsPrompt(
71 const Extension
* extension
,
72 content::BrowserContext
* context
,
74 const std::vector
<UsbDeviceFilter
>& filters
,
75 const DevicePermissionsPrompt::UsbDevicesCallback
& callback
)
76 : Prompt(extension
, context
, multiple
),
79 service_observer_(this) {}
82 ~UsbDevicePermissionsPrompt() override
{}
84 // DevicePermissionsPrompt::Prompt implementation:
86 DevicePermissionsPrompt::Prompt::Observer
* observer
) override
{
87 DevicePermissionsPrompt::Prompt::SetObserver(observer
);
90 UsbService
* service
= device::DeviceClient::Get()->GetUsbService();
91 if (service
&& !service_observer_
.IsObserving(service
)) {
93 base::Bind(&UsbDevicePermissionsPrompt::OnDevicesEnumerated
, this));
94 service_observer_
.Add(service
);
99 base::string16
GetHeading() const override
{
100 return l10n_util::GetSingleOrMultipleStringUTF16(
101 IDS_USB_DEVICE_PERMISSIONS_PROMPT_TITLE
, multiple());
104 void Dismissed() override
{
105 DevicePermissionsManager
* permissions_manager
=
106 DevicePermissionsManager::Get(browser_context());
107 std::vector
<scoped_refptr
<UsbDevice
>> devices
;
108 for (const DeviceInfo
* device
: devices_
) {
109 if (device
->granted()) {
110 const UsbDeviceInfo
* usb_device
=
111 static_cast<const UsbDeviceInfo
*>(device
);
112 devices
.push_back(usb_device
->device());
113 if (permissions_manager
) {
114 permissions_manager
->AllowUsbDevice(extension()->id(),
115 usb_device
->device());
119 DCHECK(multiple() || devices
.size() <= 1);
120 callback_
.Run(devices
);
124 // device::UsbService::Observer implementation:
125 void OnDeviceAdded(scoped_refptr
<UsbDevice
> device
) override
{
126 if (!(filters_
.empty() || UsbDeviceFilter::MatchesAny(device
, filters_
))) {
130 scoped_ptr
<DeviceInfo
> device_info(new UsbDeviceInfo(device
));
131 device
->CheckUsbAccess(
132 base::Bind(&UsbDevicePermissionsPrompt::AddCheckedDevice
, this,
133 base::Passed(&device_info
)));
136 void OnDeviceRemoved(scoped_refptr
<UsbDevice
> device
) override
{
137 for (auto it
= devices_
.begin(); it
!= devices_
.end(); ++it
) {
138 const UsbDeviceInfo
* entry
= static_cast<const UsbDeviceInfo
*>(*it
);
139 if (entry
->device() == device
) {
142 observer()->OnDevicesChanged();
149 void OnDevicesEnumerated(
150 const std::vector
<scoped_refptr
<UsbDevice
>>& devices
) {
151 for (const auto& device
: devices
) {
152 OnDeviceAdded(device
);
156 std::vector
<UsbDeviceFilter
> filters_
;
157 DevicePermissionsPrompt::UsbDevicesCallback callback_
;
158 ScopedObserver
<UsbService
, UsbService::Observer
> service_observer_
;
161 class HidDeviceInfo
: public DevicePermissionsPrompt::Prompt::DeviceInfo
{
163 HidDeviceInfo(scoped_refptr
<device::HidDeviceInfo
> device
) : device_(device
) {
164 name_
= DevicePermissionsManager::GetPermissionMessage(
165 device
->vendor_id(), device
->product_id(),
166 base::string16(), // HID devices include manufacturer in product name.
167 base::UTF8ToUTF16(device
->product_name()),
168 base::string16(), // Serial number is displayed separately.
170 serial_number_
= base::UTF8ToUTF16(device
->serial_number());
173 ~HidDeviceInfo() override
{}
175 const scoped_refptr
<device::HidDeviceInfo
>& device() const { return device_
; }
178 scoped_refptr
<device::HidDeviceInfo
> device_
;
181 class HidDevicePermissionsPrompt
: public DevicePermissionsPrompt::Prompt
,
182 public device::HidService::Observer
{
184 HidDevicePermissionsPrompt(
185 const Extension
* extension
,
186 content::BrowserContext
* context
,
188 const std::vector
<HidDeviceFilter
>& filters
,
189 const DevicePermissionsPrompt::HidDevicesCallback
& callback
)
190 : Prompt(extension
, context
, multiple
),
193 service_observer_(this) {}
196 ~HidDevicePermissionsPrompt() override
{}
198 // DevicePermissionsPrompt::Prompt implementation:
200 DevicePermissionsPrompt::Prompt::Observer
* observer
) override
{
201 DevicePermissionsPrompt::Prompt::SetObserver(observer
);
204 HidService
* service
= device::DeviceClient::Get()->GetHidService();
205 if (service
&& !service_observer_
.IsObserving(service
)) {
207 base::Bind(&HidDevicePermissionsPrompt::OnDevicesEnumerated
, this));
208 service_observer_
.Add(service
);
213 base::string16
GetHeading() const override
{
214 return l10n_util::GetSingleOrMultipleStringUTF16(
215 IDS_HID_DEVICE_PERMISSIONS_PROMPT_TITLE
, multiple());
218 void Dismissed() override
{
219 DevicePermissionsManager
* permissions_manager
=
220 DevicePermissionsManager::Get(browser_context());
221 std::vector
<scoped_refptr
<device::HidDeviceInfo
>> devices
;
222 for (const DeviceInfo
* device
: devices_
) {
223 if (device
->granted()) {
224 const HidDeviceInfo
* hid_device
=
225 static_cast<const HidDeviceInfo
*>(device
);
226 devices
.push_back(hid_device
->device());
227 if (permissions_manager
) {
228 permissions_manager
->AllowHidDevice(extension()->id(),
229 hid_device
->device());
233 DCHECK(multiple() || devices
.size() <= 1);
234 callback_
.Run(devices
);
238 // device::HidService::Observer implementation:
239 void OnDeviceAdded(scoped_refptr
<device::HidDeviceInfo
> device
) override
{
240 if (HasUnprotectedCollections(device
) &&
241 (filters_
.empty() || HidDeviceFilter::MatchesAny(device
, filters_
))) {
242 scoped_ptr
<DeviceInfo
> device_info(new HidDeviceInfo(device
));
243 #if defined(OS_CHROMEOS)
244 chromeos::PermissionBrokerClient
* client
=
245 chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
246 DCHECK(client
) << "Could not get permission broker client.";
247 device::HidDeviceInfoLinux
* linux_device_info
=
248 static_cast<device::HidDeviceInfoLinux
*>(device
.get());
249 client
->CheckPathAccess(
250 linux_device_info
->device_node(),
251 base::Bind(&HidDevicePermissionsPrompt::AddCheckedDevice
, this,
252 base::Passed(&device_info
)));
254 AddCheckedDevice(device_info
.Pass(), true);
255 #endif // defined(OS_CHROMEOS)
259 void OnDeviceRemoved(scoped_refptr
<device::HidDeviceInfo
> device
) override
{
260 for (auto it
= devices_
.begin(); it
!= devices_
.end(); ++it
) {
261 const HidDeviceInfo
* entry
= static_cast<const HidDeviceInfo
*>(*it
);
262 if (entry
->device() == device
) {
265 observer()->OnDevicesChanged();
272 void OnDevicesEnumerated(
273 const std::vector
<scoped_refptr
<device::HidDeviceInfo
>>& devices
) {
274 for (const auto& device
: devices
) {
275 OnDeviceAdded(device
);
279 bool HasUnprotectedCollections(scoped_refptr
<device::HidDeviceInfo
> device
) {
280 for (const auto& collection
: device
->collections()) {
281 if (!collection
.usage
.IsProtected()) {
288 std::vector
<HidDeviceFilter
> filters_
;
289 DevicePermissionsPrompt::HidDevicesCallback callback_
;
290 ScopedObserver
<HidService
, HidService::Observer
> service_observer_
;
295 DevicePermissionsPrompt::Prompt::DeviceInfo::DeviceInfo() {
298 DevicePermissionsPrompt::Prompt::DeviceInfo::~DeviceInfo() {
301 DevicePermissionsPrompt::Prompt::Observer::~Observer() {
304 DevicePermissionsPrompt::Prompt::Prompt(const Extension
* extension
,
305 content::BrowserContext
* context
,
307 : extension_(extension
), browser_context_(context
), multiple_(multiple
) {
310 void DevicePermissionsPrompt::Prompt::SetObserver(Observer
* observer
) {
311 observer_
= observer
;
314 base::string16
DevicePermissionsPrompt::Prompt::GetPromptMessage() const {
315 return base::i18n::MessageFormatter::FormatWithNumberedArgs(
316 l10n_util::GetStringUTF16(IDS_DEVICE_PERMISSIONS_PROMPT
),
317 multiple_
? "multiple" : "single", extension_
->name());
320 base::string16
DevicePermissionsPrompt::Prompt::GetDeviceName(
321 size_t index
) const {
322 DCHECK_LT(index
, devices_
.size());
323 return devices_
[index
]->name();
326 base::string16
DevicePermissionsPrompt::Prompt::GetDeviceSerialNumber(
327 size_t index
) const {
328 DCHECK_LT(index
, devices_
.size());
329 return devices_
[index
]->serial_number();
332 void DevicePermissionsPrompt::Prompt::GrantDevicePermission(size_t index
) {
333 DCHECK_LT(index
, devices_
.size());
334 devices_
[index
]->set_granted();
337 DevicePermissionsPrompt::Prompt::~Prompt() {
340 void DevicePermissionsPrompt::Prompt::AddCheckedDevice(
341 scoped_ptr
<DeviceInfo
> device
,
344 devices_
.push_back(device
.Pass());
346 observer_
->OnDevicesChanged();
351 DevicePermissionsPrompt::DevicePermissionsPrompt(
352 content::WebContents
* web_contents
)
353 : web_contents_(web_contents
) {
356 DevicePermissionsPrompt::~DevicePermissionsPrompt() {
359 void DevicePermissionsPrompt::AskForUsbDevices(
360 const Extension
* extension
,
361 content::BrowserContext
* context
,
363 const std::vector
<UsbDeviceFilter
>& filters
,
364 const UsbDevicesCallback
& callback
) {
365 prompt_
= new UsbDevicePermissionsPrompt(extension
, context
, multiple
,
370 void DevicePermissionsPrompt::AskForHidDevices(
371 const Extension
* extension
,
372 content::BrowserContext
* context
,
374 const std::vector
<HidDeviceFilter
>& filters
,
375 const HidDevicesCallback
& callback
) {
376 prompt_
= new HidDevicePermissionsPrompt(extension
, context
, multiple
,
382 scoped_refptr
<DevicePermissionsPrompt::Prompt
>
383 DevicePermissionsPrompt::CreateHidPromptForTest(const Extension
* extension
,
385 return make_scoped_refptr(new HidDevicePermissionsPrompt(
386 extension
, nullptr, multiple
, std::vector
<HidDeviceFilter
>(),
387 base::Bind(&NoopHidCallback
)));
391 scoped_refptr
<DevicePermissionsPrompt::Prompt
>
392 DevicePermissionsPrompt::CreateUsbPromptForTest(const Extension
* extension
,
394 return make_scoped_refptr(new UsbDevicePermissionsPrompt(
395 extension
, nullptr, multiple
, std::vector
<UsbDeviceFilter
>(),
396 base::Bind(&NoopUsbCallback
)));
399 } // namespace extensions