Delete chrome.mediaGalleriesPrivate because the functionality unique to it has since...
[chromium-blink-merge.git] / extensions / browser / api / device_permissions_manager.cc
blob3b467ebe2ecd1bf3564da1f66366057f9e30e633
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/memory/singleton.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/values.h"
12 #include "components/keyed_service/content/browser_context_dependency_manager.h"
13 #include "device/core/device_client.h"
14 #include "device/usb/usb_device.h"
15 #include "device/usb/usb_ids.h"
16 #include "extensions/browser/extension_host.h"
17 #include "extensions/browser/extension_prefs.h"
18 #include "extensions/browser/extensions_browser_client.h"
19 #include "extensions/browser/process_manager.h"
20 #include "extensions/browser/process_manager_factory.h"
21 #include "extensions/strings/grit/extensions_strings.h"
22 #include "ui/base/l10n/l10n_util.h"
24 namespace extensions {
26 using content::BrowserContext;
27 using device::UsbDevice;
28 using extensions::APIPermission;
29 using extensions::Extension;
30 using extensions::ExtensionHost;
31 using extensions::ExtensionPrefs;
33 namespace {
35 // Preference keys
37 // The device that the app has permission to access.
38 const char kDevices[] = "devices";
40 // The type of device saved.
41 const char kDeviceType[] = "type";
43 // Type identifier for USB devices.
44 const char kDeviceTypeUsb[] = "usb";
46 // The vendor ID of the device that the app had permission to access.
47 const char kDeviceVendorId[] = "vendor_id";
49 // The product ID of the device that the app had permission to access.
50 const char kDeviceProductId[] = "product_id";
52 // The serial number of the device that the app has permission to access.
53 const char kDeviceSerialNumber[] = "serial_number";
55 // The manufacturer string read from the device that the app has permission to
56 // access.
57 const char kDeviceManufacturerString[] = "manufacturer_string";
59 // The product string read from the device that the app has permission to
60 // access.
61 const char kDeviceProductString[] = "product_string";
63 // Serialized timestamp of the last time when the device was opened by the app.
64 const char kDeviceLastUsed[] = "last_used_time";
66 // Persists a DevicePermissionEntry in ExtensionPrefs.
67 void SaveDevicePermissionEntry(BrowserContext* context,
68 const std::string& extension_id,
69 scoped_refptr<DevicePermissionEntry> entry) {
70 ExtensionPrefs* prefs = ExtensionPrefs::Get(context);
71 ExtensionPrefs::ScopedListUpdate update(prefs, extension_id, kDevices);
72 base::ListValue* devices = update.Get();
73 if (!devices) {
74 devices = update.Create();
77 scoped_ptr<base::Value> device_entry(entry->ToValue());
78 DCHECK(devices->Find(*device_entry.get()) == devices->end());
79 devices->Append(device_entry.release());
82 bool MatchesDevicePermissionEntry(const base::DictionaryValue* value,
83 scoped_refptr<DevicePermissionEntry> entry) {
84 std::string type;
85 if (!value->GetStringWithoutPathExpansion(kDeviceType, &type) ||
86 type != kDeviceTypeUsb) {
87 return false;
89 int vendor_id;
90 if (!value->GetIntegerWithoutPathExpansion(kDeviceVendorId, &vendor_id) ||
91 vendor_id != entry->vendor_id()) {
92 return false;
94 int product_id;
95 if (!value->GetIntegerWithoutPathExpansion(kDeviceProductId, &product_id) ||
96 product_id != entry->product_id()) {
97 return false;
99 base::string16 serial_number;
100 if (!value->GetStringWithoutPathExpansion(kDeviceSerialNumber,
101 &serial_number) ||
102 serial_number != entry->serial_number()) {
103 return false;
105 return true;
108 // Updates the timestamp stored in ExtensionPrefs for the given
109 // DevicePermissionEntry.
110 void UpdateDevicePermissionEntry(BrowserContext* context,
111 const std::string& extension_id,
112 scoped_refptr<DevicePermissionEntry> entry) {
113 ExtensionPrefs* prefs = ExtensionPrefs::Get(context);
114 ExtensionPrefs::ScopedListUpdate update(prefs, extension_id, kDevices);
115 base::ListValue* devices = update.Get();
116 if (!devices) {
117 return;
120 for (size_t i = 0; i < devices->GetSize(); ++i) {
121 base::DictionaryValue* dict_value;
122 if (!devices->GetDictionary(i, &dict_value)) {
123 continue;
125 if (!MatchesDevicePermissionEntry(dict_value, entry)) {
126 continue;
128 devices->Set(i, entry->ToValue().release());
129 break;
133 // Removes the given DevicePermissionEntry from ExtensionPrefs.
134 void RemoveDevicePermissionEntry(BrowserContext* context,
135 const std::string& extension_id,
136 scoped_refptr<DevicePermissionEntry> entry) {
137 ExtensionPrefs* prefs = ExtensionPrefs::Get(context);
138 ExtensionPrefs::ScopedListUpdate update(prefs, extension_id, kDevices);
139 base::ListValue* devices = update.Get();
140 if (!devices) {
141 return;
144 for (size_t i = 0; i < devices->GetSize(); ++i) {
145 base::DictionaryValue* dict_value;
146 if (!devices->GetDictionary(i, &dict_value)) {
147 continue;
149 if (!MatchesDevicePermissionEntry(dict_value, entry)) {
150 continue;
152 devices->Remove(i, nullptr);
153 break;
157 // Clears all DevicePermissionEntries for the app from ExtensionPrefs.
158 void ClearDevicePermissionEntries(ExtensionPrefs* prefs,
159 const std::string& extension_id) {
160 prefs->UpdateExtensionPref(extension_id, kDevices, NULL);
163 // Returns all DevicePermissionEntries for the app.
164 std::set<scoped_refptr<DevicePermissionEntry>> GetDevicePermissionEntries(
165 ExtensionPrefs* prefs,
166 const std::string& extension_id) {
167 std::set<scoped_refptr<DevicePermissionEntry>> result;
168 const base::ListValue* devices = NULL;
169 if (!prefs->ReadPrefAsList(extension_id, kDevices, &devices)) {
170 return result;
173 for (const base::Value* entry : *devices) {
174 const base::DictionaryValue* entry_dict;
175 if (!entry->GetAsDictionary(&entry_dict)) {
176 continue;
178 std::string type;
179 if (!entry_dict->GetStringWithoutPathExpansion(kDeviceType, &type) ||
180 type != kDeviceTypeUsb) {
181 continue;
183 int vendor_id;
184 if (!entry_dict->GetIntegerWithoutPathExpansion(kDeviceVendorId,
185 &vendor_id) ||
186 vendor_id < 0 || vendor_id > UINT16_MAX) {
187 continue;
189 int product_id;
190 if (!entry_dict->GetIntegerWithoutPathExpansion(kDeviceProductId,
191 &product_id) ||
192 product_id < 0 || product_id > UINT16_MAX) {
193 continue;
195 base::string16 serial_number;
196 if (!entry_dict->GetStringWithoutPathExpansion(kDeviceSerialNumber,
197 &serial_number)) {
198 continue;
201 base::string16 manufacturer_string;
202 // Ignore failure as this string is optional.
203 entry_dict->GetStringWithoutPathExpansion(kDeviceManufacturerString,
204 &manufacturer_string);
206 base::string16 product_string;
207 // Ignore failure as this string is optional.
208 entry_dict->GetStringWithoutPathExpansion(kDeviceProductString,
209 &product_string);
211 // If a last used time is not stored in ExtensionPrefs last_used.is_null()
212 // will be true.
213 std::string last_used_str;
214 int64 last_used_i64 = 0;
215 base::Time last_used;
216 if (entry_dict->GetStringWithoutPathExpansion(kDeviceLastUsed,
217 &last_used_str) &&
218 base::StringToInt64(last_used_str, &last_used_i64)) {
219 last_used = base::Time::FromInternalValue(last_used_i64);
222 result.insert(new DevicePermissionEntry(vendor_id, product_id,
223 serial_number, manufacturer_string,
224 product_string, last_used));
226 return result;
229 } // namespace
231 DevicePermissionEntry::DevicePermissionEntry(
232 scoped_refptr<device::UsbDevice> device,
233 const base::string16& serial_number,
234 const base::string16& manufacturer_string,
235 const base::string16& product_string)
236 : device_(device),
237 vendor_id_(device->vendor_id()),
238 product_id_(device->product_id()),
239 serial_number_(serial_number),
240 manufacturer_string_(manufacturer_string),
241 product_string_(product_string) {
244 DevicePermissionEntry::DevicePermissionEntry(
245 uint16_t vendor_id,
246 uint16_t product_id,
247 const base::string16& serial_number,
248 const base::string16& manufacturer_string,
249 const base::string16& product_string,
250 const base::Time& last_used)
251 : vendor_id_(vendor_id),
252 product_id_(product_id),
253 serial_number_(serial_number),
254 manufacturer_string_(manufacturer_string),
255 product_string_(product_string),
256 last_used_(last_used) {
259 DevicePermissionEntry::~DevicePermissionEntry() {
262 bool DevicePermissionEntry::IsPersistent() const {
263 return !serial_number_.empty();
266 scoped_ptr<base::Value> DevicePermissionEntry::ToValue() const {
267 if (!IsPersistent()) {
268 return nullptr;
271 scoped_ptr<base::DictionaryValue> entry_dict(new base::DictionaryValue());
272 entry_dict->SetStringWithoutPathExpansion(kDeviceType, kDeviceTypeUsb);
273 entry_dict->SetIntegerWithoutPathExpansion(kDeviceVendorId, vendor_id_);
274 entry_dict->SetIntegerWithoutPathExpansion(kDeviceProductId, product_id_);
275 DCHECK(!serial_number_.empty());
276 entry_dict->SetStringWithoutPathExpansion(kDeviceSerialNumber,
277 serial_number_);
278 if (!manufacturer_string_.empty()) {
279 entry_dict->SetStringWithoutPathExpansion(kDeviceManufacturerString,
280 manufacturer_string_);
282 if (!product_string_.empty()) {
283 entry_dict->SetStringWithoutPathExpansion(kDeviceProductString,
284 product_string_);
286 if (!last_used_.is_null()) {
287 entry_dict->SetStringWithoutPathExpansion(
288 kDeviceLastUsed, base::Int64ToString(last_used_.ToInternalValue()));
291 return entry_dict.Pass();
294 base::string16 DevicePermissionEntry::GetPermissionMessageString() const {
295 if (serial_number_.empty()) {
296 return l10n_util::GetStringFUTF16(IDS_DEVICE_PERMISSIONS_DEVICE_NAME,
297 GetProduct(), GetManufacturer());
298 } else {
299 return l10n_util::GetStringFUTF16(
300 IDS_EXTENSION_PROMPT_WARNING_USB_DEVICE_SERIAL, GetProduct(),
301 GetManufacturer(), serial_number_);
305 base::string16 DevicePermissionEntry::GetManufacturer() const {
306 if (manufacturer_string_.empty()) {
307 const char* vendor_name = device::UsbIds::GetVendorName(vendor_id_);
308 if (vendor_name) {
309 return base::UTF8ToUTF16(vendor_name);
310 } else {
311 return l10n_util::GetStringFUTF16(
312 IDS_DEVICE_UNKNOWN_VENDOR,
313 base::ASCIIToUTF16(base::StringPrintf("0x%04x", vendor_id_)));
315 } else {
316 return manufacturer_string_;
320 base::string16 DevicePermissionEntry::GetProduct() const {
321 if (product_string_.empty()) {
322 const char* product_name =
323 device::UsbIds::GetProductName(vendor_id_, product_id_);
324 if (product_name) {
325 return base::UTF8ToUTF16(product_name);
326 } else {
327 return l10n_util::GetStringFUTF16(
328 IDS_DEVICE_UNKNOWN_PRODUCT,
329 base::ASCIIToUTF16(base::StringPrintf("0x%04x", product_id_)));
331 } else {
332 return product_string_;
336 DevicePermissions::~DevicePermissions() {
339 scoped_refptr<DevicePermissionEntry> DevicePermissions::FindEntry(
340 scoped_refptr<device::UsbDevice> device,
341 const base::string16& serial_number) const {
342 const auto& ephemeral_device_entry = ephemeral_devices_.find(device);
343 if (ephemeral_device_entry != ephemeral_devices_.end()) {
344 return ephemeral_device_entry->second;
347 if (serial_number.empty()) {
348 return nullptr;
351 for (const auto& entry : entries_) {
352 if (!entry->IsPersistent()) {
353 continue;
355 if (entry->vendor_id() != device->vendor_id()) {
356 continue;
358 if (entry->product_id() != device->product_id()) {
359 continue;
361 if (entry->serial_number() != serial_number) {
362 continue;
364 return entry;
366 return nullptr;
369 DevicePermissions::DevicePermissions(BrowserContext* context,
370 const std::string& extension_id) {
371 ExtensionPrefs* prefs = ExtensionPrefs::Get(context);
372 entries_ = GetDevicePermissionEntries(prefs, extension_id);
375 DevicePermissions::DevicePermissions(const DevicePermissions* original)
376 : entries_(original->entries_),
377 ephemeral_devices_(original->ephemeral_devices_) {
380 // static
381 DevicePermissionsManager* DevicePermissionsManager::Get(
382 BrowserContext* context) {
383 return DevicePermissionsManagerFactory::GetForBrowserContext(context);
386 scoped_ptr<DevicePermissions> DevicePermissionsManager::GetForExtension(
387 const std::string& extension_id) {
388 DCHECK(CalledOnValidThread());
389 return make_scoped_ptr(new DevicePermissions(GetOrInsert(extension_id)));
392 std::vector<base::string16>
393 DevicePermissionsManager::GetPermissionMessageStrings(
394 const std::string& extension_id) const {
395 DCHECK(CalledOnValidThread());
396 std::vector<base::string16> messages;
397 const DevicePermissions* device_permissions = Get(extension_id);
398 if (device_permissions) {
399 for (const scoped_refptr<DevicePermissionEntry>& entry :
400 device_permissions->entries()) {
401 messages.push_back(entry->GetPermissionMessageString());
404 return messages;
407 void DevicePermissionsManager::AllowUsbDevice(
408 const std::string& extension_id,
409 scoped_refptr<device::UsbDevice> device,
410 const base::string16& product_string,
411 const base::string16& manufacturer_string,
412 const base::string16& serial_number) {
413 DCHECK(CalledOnValidThread());
414 DevicePermissions* device_permissions = GetOrInsert(extension_id);
416 scoped_refptr<DevicePermissionEntry> device_entry(new DevicePermissionEntry(
417 device, serial_number, manufacturer_string, product_string));
419 if (device_entry->IsPersistent()) {
420 for (const auto& entry : device_permissions->entries()) {
421 if (entry->vendor_id() != device_entry->vendor_id()) {
422 continue;
424 if (entry->product_id() != device_entry->product_id()) {
425 continue;
427 if (entry->serial_number() == device_entry->serial_number()) {
428 return;
432 device_permissions->entries_.insert(device_entry);
433 SaveDevicePermissionEntry(context_, extension_id, device_entry);
434 } else if (!ContainsKey(device_permissions->ephemeral_devices_, device)) {
435 // Non-persistent devices cannot be reliably identified when they are
436 // reconnected so such devices are only remembered until disconnect.
437 // Register an observer here so that this set doesn't grow undefinitely.
438 device_permissions->entries_.insert(device_entry);
439 device_permissions->ephemeral_devices_[device] = device_entry;
441 // Only start observing when an ephemeral device has been added so that
442 // UsbService is not automatically initialized on profile creation (which it
443 // would be if this call were in the constructor).
444 device::UsbService* usb_service =
445 device::DeviceClient::Get()->GetUsbService();
446 DCHECK(usb_service);
447 if (!usb_service_observer_.IsObserving(usb_service)) {
448 usb_service_observer_.Add(usb_service);
453 void DevicePermissionsManager::UpdateLastUsed(
454 const std::string& extension_id,
455 scoped_refptr<DevicePermissionEntry> entry) {
456 DCHECK(CalledOnValidThread());
457 entry->set_last_used(base::Time::Now());
458 if (entry->IsPersistent()) {
459 UpdateDevicePermissionEntry(context_, extension_id, entry);
463 void DevicePermissionsManager::RemoveEntry(
464 const std::string& extension_id,
465 scoped_refptr<DevicePermissionEntry> entry) {
466 DCHECK(CalledOnValidThread());
467 DevicePermissions* device_permissions = Get(extension_id);
468 DCHECK(device_permissions);
469 DCHECK(ContainsKey(device_permissions->entries_, entry));
470 device_permissions->entries_.erase(entry);
471 if (entry->IsPersistent()) {
472 RemoveDevicePermissionEntry(context_, extension_id, entry);
473 } else {
474 device_permissions->ephemeral_devices_.erase(entry->device_);
478 void DevicePermissionsManager::Clear(const std::string& extension_id) {
479 DCHECK(CalledOnValidThread());
481 ClearDevicePermissionEntries(ExtensionPrefs::Get(context_), extension_id);
482 DevicePermissions* device_permissions = Get(extension_id);
483 if (device_permissions) {
484 extension_id_to_device_permissions_.erase(extension_id);
485 delete device_permissions;
489 DevicePermissionsManager::DevicePermissionsManager(
490 content::BrowserContext* context)
491 : context_(context),
492 process_manager_observer_(this),
493 usb_service_observer_(this) {
494 process_manager_observer_.Add(ProcessManager::Get(context));
497 DevicePermissionsManager::~DevicePermissionsManager() {
498 for (const auto& map_entry : extension_id_to_device_permissions_) {
499 DevicePermissions* device_permissions = map_entry.second;
500 delete device_permissions;
504 DevicePermissions* DevicePermissionsManager::Get(
505 const std::string& extension_id) const {
506 std::map<std::string, DevicePermissions*>::const_iterator it =
507 extension_id_to_device_permissions_.find(extension_id);
508 if (it != extension_id_to_device_permissions_.end()) {
509 return it->second;
512 return NULL;
515 DevicePermissions* DevicePermissionsManager::GetOrInsert(
516 const std::string& extension_id) {
517 DevicePermissions* device_permissions = Get(extension_id);
518 if (!device_permissions) {
519 device_permissions = new DevicePermissions(context_, extension_id);
520 extension_id_to_device_permissions_[extension_id] = device_permissions;
523 return device_permissions;
526 void DevicePermissionsManager::OnBackgroundHostClose(
527 const std::string& extension_id) {
528 DCHECK(CalledOnValidThread());
530 DevicePermissions* device_permissions = Get(extension_id);
531 if (device_permissions) {
532 // When all of the app's windows are closed and the background page is
533 // suspended all ephemeral device permissions are cleared.
534 for (const auto& map_entry : device_permissions->ephemeral_devices_) {
535 device_permissions->entries_.erase(map_entry.second);
537 device_permissions->ephemeral_devices_.clear();
541 void DevicePermissionsManager::OnDeviceRemoved(
542 scoped_refptr<UsbDevice> device) {
543 for (const auto& map_entry : extension_id_to_device_permissions_) {
544 // An ephemeral device cannot be identified if it is reconnected and so
545 // permission to access it is cleared on disconnect.
546 DevicePermissions* device_permissions = map_entry.second;
547 const auto& device_entry =
548 device_permissions->ephemeral_devices_.find(device);
549 if (device_entry != device_permissions->ephemeral_devices_.end()) {
550 device_permissions->entries_.erase(device_entry->second);
551 device_permissions->ephemeral_devices_.erase(device);
556 // static
557 DevicePermissionsManager* DevicePermissionsManagerFactory::GetForBrowserContext(
558 content::BrowserContext* context) {
559 return static_cast<DevicePermissionsManager*>(
560 GetInstance()->GetServiceForBrowserContext(context, true));
563 // static
564 DevicePermissionsManagerFactory*
565 DevicePermissionsManagerFactory::GetInstance() {
566 return Singleton<DevicePermissionsManagerFactory>::get();
569 DevicePermissionsManagerFactory::DevicePermissionsManagerFactory()
570 : BrowserContextKeyedServiceFactory(
571 "DevicePermissionsManager",
572 BrowserContextDependencyManager::GetInstance()) {
573 DependsOn(ProcessManagerFactory::GetInstance());
576 DevicePermissionsManagerFactory::~DevicePermissionsManagerFactory() {
579 KeyedService* DevicePermissionsManagerFactory::BuildServiceInstanceFor(
580 content::BrowserContext* context) const {
581 return new DevicePermissionsManager(context);
584 BrowserContext* DevicePermissionsManagerFactory::GetBrowserContextToUse(
585 BrowserContext* context) const {
586 // Return the original (possibly off-the-record) browser context so that a
587 // separate instance of the DevicePermissionsManager is used in incognito
588 // mode. The parent class's implemenation returns NULL.
589 return context;
592 } // namespace extensions