// 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_manager.h"

#include <stddef.h>

#include <utility>

#include "base/bind.h"
#include "base/memory/singleton.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "content/public/browser/browser_thread.h"
#include "device/core/device_client.h"
#include "device/hid/hid_device_info.h"
#include "device/hid/hid_service.h"
#include "device/usb/usb_device.h"
#include "device/usb/usb_ids.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/common/value_builder.h"
#include "extensions/strings/grit/extensions_strings.h"
#include "ui/base/l10n/l10n_util.h"

namespace extensions {

using content::BrowserContext;
using content::BrowserThread;
using device::HidDeviceInfo;
using device::HidService;
using device::UsbDevice;
using device::UsbService;
using extensions::APIPermission;
using extensions::Extension;
using extensions::ExtensionHost;
using extensions::ExtensionPrefs;

namespace {

// Preference keys

// The device that the app has permission to access.
const char kDevices[] = "devices";

// The type of device saved.
const char kDeviceType[] = "type";

// Type identifier for USB devices.
const char kDeviceTypeUsb[] = "usb";

// Type identifier for HID devices.
const char kDeviceTypeHid[] = "hid";

// The vendor ID of the device that the app had permission to access.
const char kDeviceVendorId[] = "vendor_id";

// The product ID of the device that the app had permission to access.
const char kDeviceProductId[] = "product_id";

// The serial number of the device that the app has permission to access.
const char kDeviceSerialNumber[] = "serial_number";

// The manufacturer string read from the device that the app has permission to
// access.
const char kDeviceManufacturerString[] = "manufacturer_string";

// The product string read from the device that the app has permission to
// access.
const char kDeviceProductString[] = "product_string";

// Serialized timestamp of the last time when the device was opened by the app.
const char kDeviceLastUsed[] = "last_used_time";

// Converts a DevicePermissionEntry::Type to a string for the prefs file.
const char* TypeToString(DevicePermissionEntry::Type type) {
  switch (type) {
    case DevicePermissionEntry::Type::USB:
      return kDeviceTypeUsb;
    case DevicePermissionEntry::Type::HID:
      return kDeviceTypeHid;
  }
  NOTREACHED();
  return "";
}

// Persists a DevicePermissionEntry in ExtensionPrefs.
void SaveDevicePermissionEntry(BrowserContext* context,
                               const std::string& extension_id,
                               scoped_refptr<DevicePermissionEntry> entry) {
  ExtensionPrefs* prefs = ExtensionPrefs::Get(context);
  ExtensionPrefs::ScopedListUpdate update(prefs, extension_id, kDevices);
  base::ListValue* devices = update.Get();
  if (!devices) {
    devices = update.Create();
  }

  std::unique_ptr<base::Value> device_entry(entry->ToValue());
  DCHECK(devices->Find(*device_entry.get()) == devices->end());
  devices->Append(std::move(device_entry));
}

bool MatchesDevicePermissionEntry(const base::DictionaryValue* value,
                                  scoped_refptr<DevicePermissionEntry> entry) {
  std::string type;
  if (!value->GetStringWithoutPathExpansion(kDeviceType, &type) ||
      type != TypeToString(entry->type())) {
    return false;
  }
  int vendor_id;
  if (!value->GetIntegerWithoutPathExpansion(kDeviceVendorId, &vendor_id) ||
      vendor_id != entry->vendor_id()) {
    return false;
  }
  int product_id;
  if (!value->GetIntegerWithoutPathExpansion(kDeviceProductId, &product_id) ||
      product_id != entry->product_id()) {
    return false;
  }
  base::string16 serial_number;
  if (!value->GetStringWithoutPathExpansion(kDeviceSerialNumber,
                                            &serial_number) ||
      serial_number != entry->serial_number()) {
    return false;
  }
  return true;
}

// Updates the timestamp stored in ExtensionPrefs for the given
// DevicePermissionEntry.
void UpdateDevicePermissionEntry(BrowserContext* context,
                                 const std::string& extension_id,
                                 scoped_refptr<DevicePermissionEntry> entry) {
  ExtensionPrefs* prefs = ExtensionPrefs::Get(context);
  ExtensionPrefs::ScopedListUpdate update(prefs, extension_id, kDevices);
  base::ListValue* devices = update.Get();
  if (!devices) {
    return;
  }

  for (size_t i = 0; i < devices->GetSize(); ++i) {
    base::DictionaryValue* dict_value;
    if (!devices->GetDictionary(i, &dict_value)) {
      continue;
    }
    if (!MatchesDevicePermissionEntry(dict_value, entry)) {
      continue;
    }
    devices->Set(i, entry->ToValue().release());
    break;
  }
}

// Removes the given DevicePermissionEntry from ExtensionPrefs.
void RemoveDevicePermissionEntry(BrowserContext* context,
                                 const std::string& extension_id,
                                 scoped_refptr<DevicePermissionEntry> entry) {
  ExtensionPrefs* prefs = ExtensionPrefs::Get(context);
  ExtensionPrefs::ScopedListUpdate update(prefs, extension_id, kDevices);
  base::ListValue* devices = update.Get();
  if (!devices) {
    return;
  }

  for (size_t i = 0; i < devices->GetSize(); ++i) {
    base::DictionaryValue* dict_value;
    if (!devices->GetDictionary(i, &dict_value)) {
      continue;
    }
    if (!MatchesDevicePermissionEntry(dict_value, entry)) {
      continue;
    }
    devices->Remove(i, nullptr);
    break;
  }
}

// Clears all DevicePermissionEntries for the app from ExtensionPrefs.
void ClearDevicePermissionEntries(ExtensionPrefs* prefs,
                                  const std::string& extension_id) {
  prefs->UpdateExtensionPref(extension_id, kDevices, NULL);
}

scoped_refptr<DevicePermissionEntry> ReadDevicePermissionEntry(
    const base::DictionaryValue* entry) {
  int vendor_id;
  if (!entry->GetIntegerWithoutPathExpansion(kDeviceVendorId, &vendor_id) ||
      vendor_id < 0 || vendor_id > UINT16_MAX) {
    return nullptr;
  }

  int product_id;
  if (!entry->GetIntegerWithoutPathExpansion(kDeviceProductId, &product_id) ||
      product_id < 0 || product_id > UINT16_MAX) {
    return nullptr;
  }

  base::string16 serial_number;
  if (!entry->GetStringWithoutPathExpansion(kDeviceSerialNumber,
                                            &serial_number)) {
    return nullptr;
  }

  base::string16 manufacturer_string;
  // Ignore failure as this string is optional.
  entry->GetStringWithoutPathExpansion(kDeviceManufacturerString,
                                       &manufacturer_string);

  base::string16 product_string;
  // Ignore failure as this string is optional.
  entry->GetStringWithoutPathExpansion(kDeviceProductString, &product_string);

  // If a last used time is not stored in ExtensionPrefs last_used.is_null()
  // will be true.
  std::string last_used_str;
  int64_t last_used_i64 = 0;
  base::Time last_used;
  if (entry->GetStringWithoutPathExpansion(kDeviceLastUsed, &last_used_str) &&
      base::StringToInt64(last_used_str, &last_used_i64)) {
    last_used = base::Time::FromInternalValue(last_used_i64);
  }

  std::string type;
  if (!entry->GetStringWithoutPathExpansion(kDeviceType, &type)) {
    return nullptr;
  }

  if (type == kDeviceTypeUsb) {
    return new DevicePermissionEntry(
        DevicePermissionEntry::Type::USB, vendor_id, product_id, serial_number,
        manufacturer_string, product_string, last_used);
  } else if (type == kDeviceTypeHid) {
    return new DevicePermissionEntry(
        DevicePermissionEntry::Type::HID, vendor_id, product_id, serial_number,
        base::string16(), product_string, last_used);
  }
  return nullptr;
}

// Returns all DevicePermissionEntries for the app.
std::set<scoped_refptr<DevicePermissionEntry>> GetDevicePermissionEntries(
    ExtensionPrefs* prefs,
    const std::string& extension_id) {
  std::set<scoped_refptr<DevicePermissionEntry>> result;
  const base::ListValue* devices = NULL;
  if (!prefs->ReadPrefAsList(extension_id, kDevices, &devices)) {
    return result;
  }

  for (const auto& entry : *devices) {
    const base::DictionaryValue* entry_dict;
    if (entry->GetAsDictionary(&entry_dict)) {
      scoped_refptr<DevicePermissionEntry> device_entry =
          ReadDevicePermissionEntry(entry_dict);
      if (entry_dict) {
        result.insert(device_entry);
      }
    }
  }
  return result;
}

}  // namespace

DevicePermissionEntry::DevicePermissionEntry(scoped_refptr<UsbDevice> device)
    : usb_device_(device),
      type_(Type::USB),
      vendor_id_(device->vendor_id()),
      product_id_(device->product_id()),
      serial_number_(device->serial_number()),
      manufacturer_string_(device->manufacturer_string()),
      product_string_(device->product_string()) {
}

DevicePermissionEntry::DevicePermissionEntry(
    scoped_refptr<HidDeviceInfo> device)
    : hid_device_(device),
      type_(Type::HID),
      vendor_id_(device->vendor_id()),
      product_id_(device->product_id()),
      serial_number_(base::UTF8ToUTF16(device->serial_number())),
      product_string_(base::UTF8ToUTF16(device->product_name())) {
}

DevicePermissionEntry::DevicePermissionEntry(
    Type type,
    uint16_t vendor_id,
    uint16_t product_id,
    const base::string16& serial_number,
    const base::string16& manufacturer_string,
    const base::string16& product_string,
    const base::Time& last_used)
    : type_(type),
      vendor_id_(vendor_id),
      product_id_(product_id),
      serial_number_(serial_number),
      manufacturer_string_(manufacturer_string),
      product_string_(product_string),
      last_used_(last_used) {
}

DevicePermissionEntry::~DevicePermissionEntry() {
}

bool DevicePermissionEntry::IsPersistent() const {
  return !serial_number_.empty();
}

std::unique_ptr<base::Value> DevicePermissionEntry::ToValue() const {
  if (!IsPersistent()) {
    return nullptr;
  }

  DCHECK(!serial_number_.empty());
  std::unique_ptr<base::DictionaryValue> entry_dict(
      DictionaryBuilder()
          .Set(kDeviceType, TypeToString(type_))
          .Set(kDeviceVendorId, vendor_id_)
          .Set(kDeviceProductId, product_id_)
          .Set(kDeviceSerialNumber, serial_number_)
          .Build());

  if (!manufacturer_string_.empty()) {
    entry_dict->SetStringWithoutPathExpansion(kDeviceManufacturerString,
                                              manufacturer_string_);
  }
  if (!product_string_.empty()) {
    entry_dict->SetStringWithoutPathExpansion(kDeviceProductString,
                                              product_string_);
  }
  if (!last_used_.is_null()) {
    entry_dict->SetStringWithoutPathExpansion(
        kDeviceLastUsed, base::Int64ToString(last_used_.ToInternalValue()));
  }

  return std::move(entry_dict);
}

base::string16 DevicePermissionEntry::GetPermissionMessageString() const {
  return DevicePermissionsManager::GetPermissionMessage(
      vendor_id_, product_id_, manufacturer_string_, product_string_,
      serial_number_, type_ == Type::USB);
}

DevicePermissions::~DevicePermissions() {
}

scoped_refptr<DevicePermissionEntry> DevicePermissions::FindUsbDeviceEntry(
    scoped_refptr<UsbDevice> device) const {
  const auto& ephemeral_device_entry =
      ephemeral_usb_devices_.find(device.get());
  if (ephemeral_device_entry != ephemeral_usb_devices_.end()) {
    return ephemeral_device_entry->second;
  }

  if (device->serial_number().empty()) {
    return nullptr;
  }

  for (const auto& entry : entries_) {
    if (entry->IsPersistent() && entry->vendor_id() == device->vendor_id() &&
        entry->product_id() == device->product_id() &&
        entry->serial_number() == device->serial_number()) {
      return entry;
    }
  }
  return nullptr;
}

scoped_refptr<DevicePermissionEntry> DevicePermissions::FindHidDeviceEntry(
    scoped_refptr<HidDeviceInfo> device) const {
  const auto& ephemeral_device_entry =
      ephemeral_hid_devices_.find(device.get());
  if (ephemeral_device_entry != ephemeral_hid_devices_.end()) {
    return ephemeral_device_entry->second;
  }

  if (device->serial_number().empty()) {
    return nullptr;
  }

  base::string16 serial_number = base::UTF8ToUTF16(device->serial_number());
  for (const auto& entry : entries_) {
    if (entry->IsPersistent() && entry->vendor_id() == device->vendor_id() &&
        entry->product_id() == device->product_id() &&
        entry->serial_number() == serial_number) {
      return entry;
    }
  }
  return nullptr;
}

DevicePermissions::DevicePermissions(BrowserContext* context,
                                     const std::string& extension_id) {
  ExtensionPrefs* prefs = ExtensionPrefs::Get(context);
  entries_ = GetDevicePermissionEntries(prefs, extension_id);
}

// static
DevicePermissionsManager* DevicePermissionsManager::Get(
    BrowserContext* context) {
  return DevicePermissionsManagerFactory::GetForBrowserContext(context);
}

// static
base::string16 DevicePermissionsManager::GetPermissionMessage(
    uint16_t vendor_id,
    uint16_t product_id,
    const base::string16& manufacturer_string,
    const base::string16& product_string,
    const base::string16& serial_number,
    bool always_include_manufacturer) {
  base::string16 product = product_string;
  if (product.empty()) {
    const char* product_name =
        device::UsbIds::GetProductName(vendor_id, product_id);
    if (product_name) {
      product = base::UTF8ToUTF16(product_name);
    }
  }

  base::string16 manufacturer = manufacturer_string;
  if (manufacturer_string.empty()) {
    const char* vendor_name = device::UsbIds::GetVendorName(vendor_id);
    if (vendor_name) {
      manufacturer = base::UTF8ToUTF16(vendor_name);
    }
  }

  if (serial_number.empty()) {
    if (product.empty()) {
      product = base::ASCIIToUTF16(base::StringPrintf("%04x", product_id));
      if (manufacturer.empty()) {
        manufacturer =
            base::ASCIIToUTF16(base::StringPrintf("%04x", vendor_id));
        return l10n_util::GetStringFUTF16(
            IDS_DEVICE_NAME_WITH_UNKNOWN_PRODUCT_UNKNOWN_VENDOR, product,
            manufacturer);
      } else {
        return l10n_util::GetStringFUTF16(
            IDS_DEVICE_NAME_WITH_UNKNOWN_PRODUCT_VENDOR, product, manufacturer);
      }
    } else {
      if (always_include_manufacturer) {
        if (manufacturer.empty()) {
          manufacturer =
              base::ASCIIToUTF16(base::StringPrintf("%04x", vendor_id));
          return l10n_util::GetStringFUTF16(
              IDS_DEVICE_NAME_WITH_PRODUCT_UNKNOWN_VENDOR, product,
              manufacturer);
        } else {
          return l10n_util::GetStringFUTF16(IDS_DEVICE_NAME_WITH_PRODUCT_VENDOR,
                                            product, manufacturer);
        }
      } else {
        return product;
      }
    }
  } else {
    if (product.empty()) {
      product = base::ASCIIToUTF16(base::StringPrintf("%04x", product_id));
      if (manufacturer.empty()) {
        manufacturer =
            base::ASCIIToUTF16(base::StringPrintf("%04x", vendor_id));
        return l10n_util::GetStringFUTF16(
            IDS_DEVICE_NAME_WITH_UNKNOWN_PRODUCT_UNKNOWN_VENDOR_SERIAL, product,
            manufacturer, serial_number);
      } else {
        return l10n_util::GetStringFUTF16(
            IDS_DEVICE_NAME_WITH_UNKNOWN_PRODUCT_VENDOR_SERIAL, product,
            manufacturer, serial_number);
      }
    } else {
      if (always_include_manufacturer) {
        if (manufacturer.empty()) {
          manufacturer =
              base::ASCIIToUTF16(base::StringPrintf("%04x", vendor_id));
          return l10n_util::GetStringFUTF16(
              IDS_DEVICE_NAME_WITH_PRODUCT_UNKNOWN_VENDOR_SERIAL, product,
              manufacturer, serial_number);
        } else {
          return l10n_util::GetStringFUTF16(
              IDS_DEVICE_NAME_WITH_PRODUCT_VENDOR_SERIAL, product, manufacturer,
              serial_number);
        }
      } else {
        return l10n_util::GetStringFUTF16(IDS_DEVICE_NAME_WITH_PRODUCT_SERIAL,
                                          product, serial_number);
      }
    }
  }
}

DevicePermissions* DevicePermissionsManager::GetForExtension(
    const std::string& extension_id) {
  DCHECK(thread_checker_.CalledOnValidThread());
  DevicePermissions* device_permissions = GetInternal(extension_id);
  if (!device_permissions) {
    device_permissions = new DevicePermissions(context_, extension_id);
    extension_id_to_device_permissions_[extension_id] = device_permissions;
  }

  return device_permissions;
}

std::vector<base::string16>
DevicePermissionsManager::GetPermissionMessageStrings(
    const std::string& extension_id) const {
  DCHECK(thread_checker_.CalledOnValidThread());
  std::vector<base::string16> messages;
  const DevicePermissions* device_permissions = GetInternal(extension_id);
  if (device_permissions) {
    for (const scoped_refptr<DevicePermissionEntry>& entry :
         device_permissions->entries()) {
      messages.push_back(entry->GetPermissionMessageString());
    }
  }
  return messages;
}

void DevicePermissionsManager::AllowUsbDevice(const std::string& extension_id,
                                              scoped_refptr<UsbDevice> device) {
  DCHECK(thread_checker_.CalledOnValidThread());
  DevicePermissions* device_permissions = GetForExtension(extension_id);

  scoped_refptr<DevicePermissionEntry> device_entry(
      new DevicePermissionEntry(device));

  if (device_entry->IsPersistent()) {
    for (const auto& entry : device_permissions->entries()) {
      if (entry->vendor_id() == device_entry->vendor_id() &&
          entry->product_id() == device_entry->product_id() &&
          entry->serial_number() == device_entry->serial_number()) {
        return;
      }
    }

    device_permissions->entries_.insert(device_entry);
    SaveDevicePermissionEntry(context_, extension_id, device_entry);
  } else if (!ContainsKey(device_permissions->ephemeral_usb_devices_,
                          device.get())) {
    // Non-persistent devices cannot be reliably identified when they are
    // reconnected so such devices are only remembered until disconnect.
    // Register an observer here so that this set doesn't grow undefinitely.
    device_permissions->entries_.insert(device_entry);
    device_permissions->ephemeral_usb_devices_[device.get()] = device_entry;

    // Only start observing when an ephemeral device has been added so that
    // UsbService is not automatically initialized on profile creation (which it
    // would be if this call were in the constructor).
    UsbService* usb_service = device::DeviceClient::Get()->GetUsbService();
    if (!usb_service_observer_.IsObserving(usb_service)) {
      usb_service_observer_.Add(usb_service);
    }
  }
}

void DevicePermissionsManager::AllowHidDevice(
    const std::string& extension_id,
    scoped_refptr<HidDeviceInfo> device) {
  DCHECK(thread_checker_.CalledOnValidThread());
  DevicePermissions* device_permissions = GetForExtension(extension_id);

  scoped_refptr<DevicePermissionEntry> device_entry(
      new DevicePermissionEntry(device));

  if (device_entry->IsPersistent()) {
    for (const auto& entry : device_permissions->entries()) {
      if (entry->vendor_id() == device_entry->vendor_id() &&
          entry->product_id() == device_entry->product_id() &&
          entry->serial_number() == device_entry->serial_number()) {
        return;
      }
    }

    device_permissions->entries_.insert(device_entry);
    SaveDevicePermissionEntry(context_, extension_id, device_entry);
  } else if (!ContainsKey(device_permissions->ephemeral_hid_devices_,
                          device.get())) {
    // Non-persistent devices cannot be reliably identified when they are
    // reconnected so such devices are only remembered until disconnect.
    // Register an observer here so that this set doesn't grow undefinitely.
    device_permissions->entries_.insert(device_entry);
    device_permissions->ephemeral_hid_devices_[device.get()] = device_entry;

    // Only start observing when an ephemeral device has been added so that
    // HidService is not automatically initialized on profile creation (which it
    // would be if this call were in the constructor).
    HidService* hid_service = device::DeviceClient::Get()->GetHidService();
    if (!hid_service_observer_.IsObserving(hid_service)) {
      hid_service_observer_.Add(hid_service);
    }
  }
}

void DevicePermissionsManager::UpdateLastUsed(
    const std::string& extension_id,
    scoped_refptr<DevicePermissionEntry> entry) {
  DCHECK(thread_checker_.CalledOnValidThread());
  entry->set_last_used(base::Time::Now());
  if (entry->IsPersistent()) {
    UpdateDevicePermissionEntry(context_, extension_id, entry);
  }
}

void DevicePermissionsManager::RemoveEntry(
    const std::string& extension_id,
    scoped_refptr<DevicePermissionEntry> entry) {
  DCHECK(thread_checker_.CalledOnValidThread());
  DevicePermissions* device_permissions = GetInternal(extension_id);
  DCHECK(device_permissions);
  DCHECK(ContainsKey(device_permissions->entries_, entry));
  device_permissions->entries_.erase(entry);
  if (entry->IsPersistent()) {
    RemoveDevicePermissionEntry(context_, extension_id, entry);
  } else if (entry->type_ == DevicePermissionEntry::Type::USB) {
    device_permissions->ephemeral_usb_devices_.erase(entry->usb_device_.get());
  } else if (entry->type_ == DevicePermissionEntry::Type::HID) {
    device_permissions->ephemeral_hid_devices_.erase(entry->hid_device_.get());
  } else {
    NOTREACHED();
  }
}

void DevicePermissionsManager::Clear(const std::string& extension_id) {
  DCHECK(thread_checker_.CalledOnValidThread());

  ClearDevicePermissionEntries(ExtensionPrefs::Get(context_), extension_id);
  DevicePermissions* device_permissions = GetInternal(extension_id);
  if (device_permissions) {
    extension_id_to_device_permissions_.erase(extension_id);
    delete device_permissions;
  }
}

DevicePermissionsManager::DevicePermissionsManager(
    content::BrowserContext* context)
    : context_(context),
      usb_service_observer_(this),
      hid_service_observer_(this) {
}

DevicePermissionsManager::~DevicePermissionsManager() {
  for (const auto& map_entry : extension_id_to_device_permissions_) {
    DevicePermissions* device_permissions = map_entry.second;
    delete device_permissions;
  }
}

DevicePermissions* DevicePermissionsManager::GetInternal(
    const std::string& extension_id) const {
  std::map<std::string, DevicePermissions*>::const_iterator it =
      extension_id_to_device_permissions_.find(extension_id);
  if (it != extension_id_to_device_permissions_.end()) {
    return it->second;
  }

  return NULL;
}

void DevicePermissionsManager::OnDeviceRemovedCleanup(
    scoped_refptr<UsbDevice> device) {
  DCHECK(thread_checker_.CalledOnValidThread());
  for (const auto& map_entry : extension_id_to_device_permissions_) {
    // An ephemeral device cannot be identified if it is reconnected and so
    // permission to access it is cleared on disconnect.
    DevicePermissions* device_permissions = map_entry.second;
    const auto& device_entry =
        device_permissions->ephemeral_usb_devices_.find(device.get());
    if (device_entry != device_permissions->ephemeral_usb_devices_.end()) {
      device_permissions->entries_.erase(device_entry->second);
      device_permissions->ephemeral_usb_devices_.erase(device_entry);
    }
  }
}

void DevicePermissionsManager::OnDeviceRemovedCleanup(
    scoped_refptr<device::HidDeviceInfo> device) {
  DCHECK(thread_checker_.CalledOnValidThread());
  for (const auto& map_entry : extension_id_to_device_permissions_) {
    // An ephemeral device cannot be identified if it is reconnected and so
    // permission to access it is cleared on disconnect.
    DevicePermissions* device_permissions = map_entry.second;
    const auto& device_entry =
        device_permissions->ephemeral_hid_devices_.find(device.get());
    if (device_entry != device_permissions->ephemeral_hid_devices_.end()) {
      device_permissions->entries_.erase(device_entry->second);
      device_permissions->ephemeral_hid_devices_.erase(device_entry);
    }
  }
}

// static
DevicePermissionsManager* DevicePermissionsManagerFactory::GetForBrowserContext(
    content::BrowserContext* context) {
  return static_cast<DevicePermissionsManager*>(
      GetInstance()->GetServiceForBrowserContext(context, true));
}

// static
DevicePermissionsManagerFactory*
DevicePermissionsManagerFactory::GetInstance() {
  return base::Singleton<DevicePermissionsManagerFactory>::get();
}

DevicePermissionsManagerFactory::DevicePermissionsManagerFactory()
    : BrowserContextKeyedServiceFactory(
          "DevicePermissionsManager",
          BrowserContextDependencyManager::GetInstance()) {
}

DevicePermissionsManagerFactory::~DevicePermissionsManagerFactory() {
}

KeyedService* DevicePermissionsManagerFactory::BuildServiceInstanceFor(
    content::BrowserContext* context) const {
  return new DevicePermissionsManager(context);
}

BrowserContext* DevicePermissionsManagerFactory::GetBrowserContextToUse(
    BrowserContext* context) const {
  // Return the original (possibly off-the-record) browser context so that a
  // separate instance of the DevicePermissionsManager is used in incognito
  // mode. The parent class's implemenation returns NULL.
  return context;
}

}  // namespace extensions
