Update {virtual,override,final} to follow C++11 style.
[chromium-blink-merge.git] / extensions / browser / api / device_permissions_manager.cc
blobbaa3c04b6d07862959b56d1323501f82486bb97d
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"
7 #include "base/bind.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;
37 namespace {
39 // Preference keys
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
60 // access.
61 const char kDeviceManufacturerString[] = "manufacturer_string";
63 // The product string read from the device that the app has permission to
64 // access.
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();
77 if (!devices) {
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) {
88 std::string type;
89 if (!value->GetStringWithoutPathExpansion(kDeviceType, &type) ||
90 type != kDeviceTypeUsb) {
91 return false;
93 int vendor_id;
94 if (!value->GetIntegerWithoutPathExpansion(kDeviceVendorId, &vendor_id) ||
95 vendor_id != entry->vendor_id()) {
96 return false;
98 int product_id;
99 if (!value->GetIntegerWithoutPathExpansion(kDeviceProductId, &product_id) ||
100 product_id != entry->product_id()) {
101 return false;
103 base::string16 serial_number;
104 if (!value->GetStringWithoutPathExpansion(kDeviceSerialNumber,
105 &serial_number) ||
106 serial_number != entry->serial_number()) {
107 return false;
109 return true;
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();
120 if (!devices) {
121 return;
124 for (size_t i = 0; i < devices->GetSize(); ++i) {
125 base::DictionaryValue* dict_value;
126 if (!devices->GetDictionary(i, &dict_value)) {
127 continue;
129 if (!MatchesDevicePermissionEntry(dict_value, entry)) {
130 continue;
132 devices->Set(i, entry->ToValue().release());
133 break;
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();
144 if (!devices) {
145 return;
148 for (size_t i = 0; i < devices->GetSize(); ++i) {
149 base::DictionaryValue* dict_value;
150 if (!devices->GetDictionary(i, &dict_value)) {
151 continue;
153 if (!MatchesDevicePermissionEntry(dict_value, entry)) {
154 continue;
156 devices->Remove(i, nullptr);
157 break;
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)) {
174 return result;
177 for (const base::Value* entry : *devices) {
178 const base::DictionaryValue* entry_dict;
179 if (!entry->GetAsDictionary(&entry_dict)) {
180 continue;
182 std::string type;
183 if (!entry_dict->GetStringWithoutPathExpansion(kDeviceType, &type) ||
184 type != kDeviceTypeUsb) {
185 continue;
187 int vendor_id;
188 if (!entry_dict->GetIntegerWithoutPathExpansion(kDeviceVendorId,
189 &vendor_id) ||
190 vendor_id < 0 || vendor_id > UINT16_MAX) {
191 continue;
193 int product_id;
194 if (!entry_dict->GetIntegerWithoutPathExpansion(kDeviceProductId,
195 &product_id) ||
196 product_id < 0 || product_id > UINT16_MAX) {
197 continue;
199 base::string16 serial_number;
200 if (!entry_dict->GetStringWithoutPathExpansion(kDeviceSerialNumber,
201 &serial_number)) {
202 continue;
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,
213 &product_string);
215 // If a last used time is not stored in ExtensionPrefs last_used.is_null()
216 // will be true.
217 std::string last_used_str;
218 int64 last_used_i64 = 0;
219 base::Time last_used;
220 if (entry_dict->GetStringWithoutPathExpansion(kDeviceLastUsed,
221 &last_used_str) &&
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));
230 return result;
233 } // namespace
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)
240 : device_(device),
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(
249 uint16_t vendor_id,
250 uint16_t product_id,
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()) {
272 return nullptr;
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,
281 serial_number_);
282 if (!manufacturer_string_.empty()) {
283 entry_dict->SetStringWithoutPathExpansion(kDeviceManufacturerString,
284 manufacturer_string_);
286 if (!product_string_.empty()) {
287 entry_dict->SetStringWithoutPathExpansion(kDeviceProductString,
288 product_string_);
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());
302 } else {
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_);
312 if (vendor_name) {
313 return base::UTF8ToUTF16(vendor_name);
314 } else {
315 return l10n_util::GetStringFUTF16(
316 IDS_DEVICE_UNKNOWN_VENDOR,
317 base::ASCIIToUTF16(base::StringPrintf("0x%04x", vendor_id_)));
319 } else {
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_);
328 if (product_name) {
329 return base::UTF8ToUTF16(product_name);
330 } else {
331 return l10n_util::GetStringFUTF16(
332 IDS_DEVICE_UNKNOWN_PRODUCT,
333 base::ASCIIToUTF16(base::StringPrintf("0x%04x", product_id_)));
335 } else {
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()) {
352 return nullptr;
355 for (const auto& entry : entries_) {
356 if (!entry->IsPersistent()) {
357 continue;
359 if (entry->vendor_id() != device->vendor_id()) {
360 continue;
362 if (entry->product_id() != device->product_id()) {
363 continue;
365 if (entry->serial_number() != serial_number) {
366 continue;
368 return entry;
370 return nullptr;
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 {
385 public:
386 FileThreadHelper(
387 base::WeakPtr<DevicePermissionsManager> device_permissions_manager)
388 : device_permissions_manager_(device_permissions_manager),
389 observer_(this) {}
390 virtual ~FileThreadHelper() {}
392 void Start() {
393 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
394 UsbService* service = device::DeviceClient::Get()->GetUsbService();
395 if (service) {
396 observer_.Add(service);
400 private:
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_;
413 // static
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());
437 return messages;
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()) {
455 continue;
457 if (entry->product_id() != device_entry->product_id()) {
458 continue;
460 if (entry->serial_number() == device_entry->serial_number()) {
461 return;
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).
477 if (!helper_) {
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);
508 } else {
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)
526 : context_(context),
527 process_manager_observer_(this),
528 helper_(nullptr),
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;
538 if (helper_) {
539 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, helper_);
540 helper_ = nullptr;
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()) {
549 return it->second;
552 return NULL;
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);
597 // static
598 DevicePermissionsManager* DevicePermissionsManagerFactory::GetForBrowserContext(
599 content::BrowserContext* context) {
600 return static_cast<DevicePermissionsManager*>(
601 GetInstance()->GetServiceForBrowserContext(context, true));
604 // static
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.
630 return context;
633 } // namespace extensions