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_manager.h"
8 #include "base/memory/singleton.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/values.h"
13 #include "components/keyed_service/content/browser_context_dependency_manager.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "device/core/device_client.h"
16 #include "device/usb/usb_device.h"
17 #include "device/usb/usb_ids.h"
18 #include "extensions/browser/extension_host.h"
19 #include "extensions/browser/extension_prefs.h"
20 #include "extensions/browser/extensions_browser_client.h"
21 #include "extensions/browser/process_manager.h"
22 #include "extensions/browser/process_manager_factory.h"
23 #include "extensions/strings/grit/extensions_strings.h"
24 #include "ui/base/l10n/l10n_util.h"
26 namespace extensions
{
28 using content::BrowserContext
;
29 using content::BrowserThread
;
30 using device::UsbDevice
;
31 using device::UsbService
;
32 using extensions::APIPermission
;
33 using extensions::Extension
;
34 using extensions::ExtensionHost
;
35 using extensions::ExtensionPrefs
;
41 // The device that the app has permission to access.
42 const char kDevices
[] = "devices";
44 // The type of device saved.
45 const char kDeviceType
[] = "type";
47 // Type identifier for USB devices.
48 const char kDeviceTypeUsb
[] = "usb";
50 // The vendor ID of the device that the app had permission to access.
51 const char kDeviceVendorId
[] = "vendor_id";
53 // The product ID of the device that the app had permission to access.
54 const char kDeviceProductId
[] = "product_id";
56 // The serial number of the device that the app has permission to access.
57 const char kDeviceSerialNumber
[] = "serial_number";
59 // The manufacturer string read from the device that the app has permission to
61 const char kDeviceManufacturerString
[] = "manufacturer_string";
63 // The product string read from the device that the app has permission to
65 const char kDeviceProductString
[] = "product_string";
67 // Serialized timestamp of the last time when the device was opened by the app.
68 const char kDeviceLastUsed
[] = "last_used_time";
70 // Persists a DevicePermissionEntry in ExtensionPrefs.
71 void SaveDevicePermissionEntry(BrowserContext
* context
,
72 const std::string
& extension_id
,
73 scoped_refptr
<DevicePermissionEntry
> entry
) {
74 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(context
);
75 ExtensionPrefs::ScopedListUpdate
update(prefs
, extension_id
, kDevices
);
76 base::ListValue
* devices
= update
.Get();
78 devices
= update
.Create();
81 scoped_ptr
<base::Value
> device_entry(entry
->ToValue());
82 DCHECK(devices
->Find(*device_entry
.get()) == devices
->end());
83 devices
->Append(device_entry
.release());
86 bool MatchesDevicePermissionEntry(const base::DictionaryValue
* value
,
87 scoped_refptr
<DevicePermissionEntry
> entry
) {
89 if (!value
->GetStringWithoutPathExpansion(kDeviceType
, &type
) ||
90 type
!= kDeviceTypeUsb
) {
94 if (!value
->GetIntegerWithoutPathExpansion(kDeviceVendorId
, &vendor_id
) ||
95 vendor_id
!= entry
->vendor_id()) {
99 if (!value
->GetIntegerWithoutPathExpansion(kDeviceProductId
, &product_id
) ||
100 product_id
!= entry
->product_id()) {
103 base::string16 serial_number
;
104 if (!value
->GetStringWithoutPathExpansion(kDeviceSerialNumber
,
106 serial_number
!= entry
->serial_number()) {
112 // Updates the timestamp stored in ExtensionPrefs for the given
113 // DevicePermissionEntry.
114 void UpdateDevicePermissionEntry(BrowserContext
* context
,
115 const std::string
& extension_id
,
116 scoped_refptr
<DevicePermissionEntry
> entry
) {
117 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(context
);
118 ExtensionPrefs::ScopedListUpdate
update(prefs
, extension_id
, kDevices
);
119 base::ListValue
* devices
= update
.Get();
124 for (size_t i
= 0; i
< devices
->GetSize(); ++i
) {
125 base::DictionaryValue
* dict_value
;
126 if (!devices
->GetDictionary(i
, &dict_value
)) {
129 if (!MatchesDevicePermissionEntry(dict_value
, entry
)) {
132 devices
->Set(i
, entry
->ToValue().release());
137 // Removes the given DevicePermissionEntry from ExtensionPrefs.
138 void RemoveDevicePermissionEntry(BrowserContext
* context
,
139 const std::string
& extension_id
,
140 scoped_refptr
<DevicePermissionEntry
> entry
) {
141 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(context
);
142 ExtensionPrefs::ScopedListUpdate
update(prefs
, extension_id
, kDevices
);
143 base::ListValue
* devices
= update
.Get();
148 for (size_t i
= 0; i
< devices
->GetSize(); ++i
) {
149 base::DictionaryValue
* dict_value
;
150 if (!devices
->GetDictionary(i
, &dict_value
)) {
153 if (!MatchesDevicePermissionEntry(dict_value
, entry
)) {
156 devices
->Remove(i
, nullptr);
161 // Clears all DevicePermissionEntries for the app from ExtensionPrefs.
162 void ClearDevicePermissionEntries(ExtensionPrefs
* prefs
,
163 const std::string
& extension_id
) {
164 prefs
->UpdateExtensionPref(extension_id
, kDevices
, NULL
);
167 // Returns all DevicePermissionEntries for the app.
168 std::set
<scoped_refptr
<DevicePermissionEntry
>> GetDevicePermissionEntries(
169 ExtensionPrefs
* prefs
,
170 const std::string
& extension_id
) {
171 std::set
<scoped_refptr
<DevicePermissionEntry
>> result
;
172 const base::ListValue
* devices
= NULL
;
173 if (!prefs
->ReadPrefAsList(extension_id
, kDevices
, &devices
)) {
177 for (const base::Value
* entry
: *devices
) {
178 const base::DictionaryValue
* entry_dict
;
179 if (!entry
->GetAsDictionary(&entry_dict
)) {
183 if (!entry_dict
->GetStringWithoutPathExpansion(kDeviceType
, &type
) ||
184 type
!= kDeviceTypeUsb
) {
188 if (!entry_dict
->GetIntegerWithoutPathExpansion(kDeviceVendorId
,
190 vendor_id
< 0 || vendor_id
> UINT16_MAX
) {
194 if (!entry_dict
->GetIntegerWithoutPathExpansion(kDeviceProductId
,
196 product_id
< 0 || product_id
> UINT16_MAX
) {
199 base::string16 serial_number
;
200 if (!entry_dict
->GetStringWithoutPathExpansion(kDeviceSerialNumber
,
205 base::string16 manufacturer_string
;
206 // Ignore failure as this string is optional.
207 entry_dict
->GetStringWithoutPathExpansion(kDeviceManufacturerString
,
208 &manufacturer_string
);
210 base::string16 product_string
;
211 // Ignore failure as this string is optional.
212 entry_dict
->GetStringWithoutPathExpansion(kDeviceProductString
,
215 // If a last used time is not stored in ExtensionPrefs last_used.is_null()
217 std::string last_used_str
;
218 int64 last_used_i64
= 0;
219 base::Time last_used
;
220 if (entry_dict
->GetStringWithoutPathExpansion(kDeviceLastUsed
,
222 base::StringToInt64(last_used_str
, &last_used_i64
)) {
223 last_used
= base::Time::FromInternalValue(last_used_i64
);
226 result
.insert(new DevicePermissionEntry(vendor_id
, product_id
,
227 serial_number
, manufacturer_string
,
228 product_string
, last_used
));
235 DevicePermissionEntry::DevicePermissionEntry(
236 scoped_refptr
<device::UsbDevice
> device
,
237 const base::string16
& serial_number
,
238 const base::string16
& manufacturer_string
,
239 const base::string16
& product_string
)
241 vendor_id_(device
->vendor_id()),
242 product_id_(device
->product_id()),
243 serial_number_(serial_number
),
244 manufacturer_string_(manufacturer_string
),
245 product_string_(product_string
) {
248 DevicePermissionEntry::DevicePermissionEntry(
251 const base::string16
& serial_number
,
252 const base::string16
& manufacturer_string
,
253 const base::string16
& product_string
,
254 const base::Time
& last_used
)
255 : vendor_id_(vendor_id
),
256 product_id_(product_id
),
257 serial_number_(serial_number
),
258 manufacturer_string_(manufacturer_string
),
259 product_string_(product_string
),
260 last_used_(last_used
) {
263 DevicePermissionEntry::~DevicePermissionEntry() {
266 bool DevicePermissionEntry::IsPersistent() const {
267 return !serial_number_
.empty();
270 scoped_ptr
<base::Value
> DevicePermissionEntry::ToValue() const {
271 if (!IsPersistent()) {
275 scoped_ptr
<base::DictionaryValue
> entry_dict(new base::DictionaryValue());
276 entry_dict
->SetStringWithoutPathExpansion(kDeviceType
, kDeviceTypeUsb
);
277 entry_dict
->SetIntegerWithoutPathExpansion(kDeviceVendorId
, vendor_id_
);
278 entry_dict
->SetIntegerWithoutPathExpansion(kDeviceProductId
, product_id_
);
279 DCHECK(!serial_number_
.empty());
280 entry_dict
->SetStringWithoutPathExpansion(kDeviceSerialNumber
,
282 if (!manufacturer_string_
.empty()) {
283 entry_dict
->SetStringWithoutPathExpansion(kDeviceManufacturerString
,
284 manufacturer_string_
);
286 if (!product_string_
.empty()) {
287 entry_dict
->SetStringWithoutPathExpansion(kDeviceProductString
,
290 if (!last_used_
.is_null()) {
291 entry_dict
->SetStringWithoutPathExpansion(
292 kDeviceLastUsed
, base::Int64ToString(last_used_
.ToInternalValue()));
295 return entry_dict
.Pass();
298 base::string16
DevicePermissionEntry::GetPermissionMessageString() const {
299 if (serial_number_
.empty()) {
300 return l10n_util::GetStringFUTF16(IDS_DEVICE_PERMISSIONS_DEVICE_NAME
,
301 GetProduct(), GetManufacturer());
303 return l10n_util::GetStringFUTF16(
304 IDS_EXTENSION_PROMPT_WARNING_USB_DEVICE_SERIAL
, GetProduct(),
305 GetManufacturer(), serial_number_
);
309 base::string16
DevicePermissionEntry::GetManufacturer() const {
310 if (manufacturer_string_
.empty()) {
311 const char* vendor_name
= device::UsbIds::GetVendorName(vendor_id_
);
313 return base::UTF8ToUTF16(vendor_name
);
315 return l10n_util::GetStringFUTF16(
316 IDS_DEVICE_UNKNOWN_VENDOR
,
317 base::ASCIIToUTF16(base::StringPrintf("0x%04x", vendor_id_
)));
320 return manufacturer_string_
;
324 base::string16
DevicePermissionEntry::GetProduct() const {
325 if (product_string_
.empty()) {
326 const char* product_name
=
327 device::UsbIds::GetProductName(vendor_id_
, product_id_
);
329 return base::UTF8ToUTF16(product_name
);
331 return l10n_util::GetStringFUTF16(
332 IDS_DEVICE_UNKNOWN_PRODUCT
,
333 base::ASCIIToUTF16(base::StringPrintf("0x%04x", product_id_
)));
336 return product_string_
;
340 DevicePermissions::~DevicePermissions() {
343 scoped_refptr
<DevicePermissionEntry
> DevicePermissions::FindEntry(
344 scoped_refptr
<device::UsbDevice
> device
,
345 const base::string16
& serial_number
) const {
346 const auto& ephemeral_device_entry
= ephemeral_devices_
.find(device
);
347 if (ephemeral_device_entry
!= ephemeral_devices_
.end()) {
348 return ephemeral_device_entry
->second
;
351 if (serial_number
.empty()) {
355 for (const auto& entry
: entries_
) {
356 if (!entry
->IsPersistent()) {
359 if (entry
->vendor_id() != device
->vendor_id()) {
362 if (entry
->product_id() != device
->product_id()) {
365 if (entry
->serial_number() != serial_number
) {
373 DevicePermissions::DevicePermissions(BrowserContext
* context
,
374 const std::string
& extension_id
) {
375 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(context
);
376 entries_
= GetDevicePermissionEntries(prefs
, extension_id
);
379 DevicePermissions::DevicePermissions(const DevicePermissions
* original
)
380 : entries_(original
->entries_
),
381 ephemeral_devices_(original
->ephemeral_devices_
) {
384 class DevicePermissionsManager::FileThreadHelper
: public UsbService::Observer
{
387 base::WeakPtr
<DevicePermissionsManager
> device_permissions_manager
)
388 : device_permissions_manager_(device_permissions_manager
),
390 virtual ~FileThreadHelper() {}
393 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
394 UsbService
* service
= device::DeviceClient::Get()->GetUsbService();
396 observer_
.Add(service
);
401 void OnDeviceRemoved(scoped_refptr
<UsbDevice
> device
) override
{
402 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
403 BrowserThread::PostTask(
404 BrowserThread::UI
, FROM_HERE
,
405 base::Bind(&DevicePermissionsManager::OnDeviceRemoved
,
406 device_permissions_manager_
, device
));
409 base::WeakPtr
<DevicePermissionsManager
> device_permissions_manager_
;
410 ScopedObserver
<UsbService
, UsbService::Observer
> observer_
;
414 DevicePermissionsManager
* DevicePermissionsManager::Get(
415 BrowserContext
* context
) {
416 return DevicePermissionsManagerFactory::GetForBrowserContext(context
);
419 scoped_ptr
<DevicePermissions
> DevicePermissionsManager::GetForExtension(
420 const std::string
& extension_id
) {
421 DCHECK(CalledOnValidThread());
422 return make_scoped_ptr(new DevicePermissions(GetOrInsert(extension_id
)));
425 std::vector
<base::string16
>
426 DevicePermissionsManager::GetPermissionMessageStrings(
427 const std::string
& extension_id
) const {
428 DCHECK(CalledOnValidThread());
429 std::vector
<base::string16
> messages
;
430 const DevicePermissions
* device_permissions
= Get(extension_id
);
431 if (device_permissions
) {
432 for (const scoped_refptr
<DevicePermissionEntry
>& entry
:
433 device_permissions
->entries()) {
434 messages
.push_back(entry
->GetPermissionMessageString());
440 void DevicePermissionsManager::AllowUsbDevice(
441 const std::string
& extension_id
,
442 scoped_refptr
<device::UsbDevice
> device
,
443 const base::string16
& product_string
,
444 const base::string16
& manufacturer_string
,
445 const base::string16
& serial_number
) {
446 DCHECK(CalledOnValidThread());
447 DevicePermissions
* device_permissions
= GetOrInsert(extension_id
);
449 scoped_refptr
<DevicePermissionEntry
> device_entry(new DevicePermissionEntry(
450 device
, serial_number
, manufacturer_string
, product_string
));
452 if (device_entry
->IsPersistent()) {
453 for (const auto& entry
: device_permissions
->entries()) {
454 if (entry
->vendor_id() != device_entry
->vendor_id()) {
457 if (entry
->product_id() != device_entry
->product_id()) {
460 if (entry
->serial_number() == device_entry
->serial_number()) {
465 device_permissions
->entries_
.insert(device_entry
);
466 SaveDevicePermissionEntry(context_
, extension_id
, device_entry
);
467 } else if (!ContainsKey(device_permissions
->ephemeral_devices_
, device
)) {
468 // Non-persistent devices cannot be reliably identified when they are
469 // reconnected so such devices are only remembered until disconnect.
470 // Register an observer here so that this set doesn't grow undefinitely.
471 device_permissions
->entries_
.insert(device_entry
);
472 device_permissions
->ephemeral_devices_
[device
] = device_entry
;
474 // Only start observing when an ephemeral device has been added so that
475 // UsbService is not automatically initialized on profile creation (which it
476 // would be if this call were in the constructor).
478 helper_
= new FileThreadHelper(weak_factory_
.GetWeakPtr());
479 // base::Unretained is safe because any task to delete helper_ will be
480 // executed after this call.
481 BrowserThread::PostTask(
482 BrowserThread::FILE, FROM_HERE
,
483 base::Bind(&FileThreadHelper::Start
, base::Unretained(helper_
)));
488 void DevicePermissionsManager::UpdateLastUsed(
489 const std::string
& extension_id
,
490 scoped_refptr
<DevicePermissionEntry
> entry
) {
491 DCHECK(CalledOnValidThread());
492 entry
->set_last_used(base::Time::Now());
493 if (entry
->IsPersistent()) {
494 UpdateDevicePermissionEntry(context_
, extension_id
, entry
);
498 void DevicePermissionsManager::RemoveEntry(
499 const std::string
& extension_id
,
500 scoped_refptr
<DevicePermissionEntry
> entry
) {
501 DCHECK(CalledOnValidThread());
502 DevicePermissions
* device_permissions
= Get(extension_id
);
503 DCHECK(device_permissions
);
504 DCHECK(ContainsKey(device_permissions
->entries_
, entry
));
505 device_permissions
->entries_
.erase(entry
);
506 if (entry
->IsPersistent()) {
507 RemoveDevicePermissionEntry(context_
, extension_id
, entry
);
509 device_permissions
->ephemeral_devices_
.erase(entry
->device_
);
513 void DevicePermissionsManager::Clear(const std::string
& extension_id
) {
514 DCHECK(CalledOnValidThread());
516 ClearDevicePermissionEntries(ExtensionPrefs::Get(context_
), extension_id
);
517 DevicePermissions
* device_permissions
= Get(extension_id
);
518 if (device_permissions
) {
519 extension_id_to_device_permissions_
.erase(extension_id
);
520 delete device_permissions
;
524 DevicePermissionsManager::DevicePermissionsManager(
525 content::BrowserContext
* context
)
527 process_manager_observer_(this),
529 weak_factory_(this) {
530 process_manager_observer_
.Add(ProcessManager::Get(context
));
533 DevicePermissionsManager::~DevicePermissionsManager() {
534 for (const auto& map_entry
: extension_id_to_device_permissions_
) {
535 DevicePermissions
* device_permissions
= map_entry
.second
;
536 delete device_permissions
;
539 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE
, helper_
);
544 DevicePermissions
* DevicePermissionsManager::Get(
545 const std::string
& extension_id
) const {
546 std::map
<std::string
, DevicePermissions
*>::const_iterator it
=
547 extension_id_to_device_permissions_
.find(extension_id
);
548 if (it
!= extension_id_to_device_permissions_
.end()) {
555 DevicePermissions
* DevicePermissionsManager::GetOrInsert(
556 const std::string
& extension_id
) {
557 DevicePermissions
* device_permissions
= Get(extension_id
);
558 if (!device_permissions
) {
559 device_permissions
= new DevicePermissions(context_
, extension_id
);
560 extension_id_to_device_permissions_
[extension_id
] = device_permissions
;
563 return device_permissions
;
566 void DevicePermissionsManager::OnBackgroundHostClose(
567 const std::string
& extension_id
) {
568 DCHECK(CalledOnValidThread());
570 DevicePermissions
* device_permissions
= Get(extension_id
);
571 if (device_permissions
) {
572 // When all of the app's windows are closed and the background page is
573 // suspended all ephemeral device permissions are cleared.
574 for (const auto& map_entry
: device_permissions
->ephemeral_devices_
) {
575 device_permissions
->entries_
.erase(map_entry
.second
);
577 device_permissions
->ephemeral_devices_
.clear();
581 void DevicePermissionsManager::OnDeviceRemoved(
582 scoped_refptr
<UsbDevice
> device
) {
583 DCHECK(CalledOnValidThread());
584 for (const auto& map_entry
: extension_id_to_device_permissions_
) {
585 // An ephemeral device cannot be identified if it is reconnected and so
586 // permission to access it is cleared on disconnect.
587 DevicePermissions
* device_permissions
= map_entry
.second
;
588 const auto& device_entry
=
589 device_permissions
->ephemeral_devices_
.find(device
);
590 if (device_entry
!= device_permissions
->ephemeral_devices_
.end()) {
591 device_permissions
->entries_
.erase(device_entry
->second
);
592 device_permissions
->ephemeral_devices_
.erase(device
);
598 DevicePermissionsManager
* DevicePermissionsManagerFactory::GetForBrowserContext(
599 content::BrowserContext
* context
) {
600 return static_cast<DevicePermissionsManager
*>(
601 GetInstance()->GetServiceForBrowserContext(context
, true));
605 DevicePermissionsManagerFactory
*
606 DevicePermissionsManagerFactory::GetInstance() {
607 return Singleton
<DevicePermissionsManagerFactory
>::get();
610 DevicePermissionsManagerFactory::DevicePermissionsManagerFactory()
611 : BrowserContextKeyedServiceFactory(
612 "DevicePermissionsManager",
613 BrowserContextDependencyManager::GetInstance()) {
614 DependsOn(ProcessManagerFactory::GetInstance());
617 DevicePermissionsManagerFactory::~DevicePermissionsManagerFactory() {
620 KeyedService
* DevicePermissionsManagerFactory::BuildServiceInstanceFor(
621 content::BrowserContext
* context
) const {
622 return new DevicePermissionsManager(context
);
625 BrowserContext
* DevicePermissionsManagerFactory::GetBrowserContextToUse(
626 BrowserContext
* context
) const {
627 // Return the original (possibly off-the-record) browser context so that a
628 // separate instance of the DevicePermissionsManager is used in incognito
629 // mode. The parent class's implemenation returns NULL.
633 } // namespace extensions