Include policy USB devices in GetAllGrantedObjects

This change updates the GetAllGrantedObjects() method of
UsbChooserContext to include the USB objects granted by the
WebUsbAllowDevicesForUrls policy. The GetAllGrantedObjects() method
removes user granted objects that are also granted permission by the
policy.

SiteSettingsHelper was modified to skip the policy objects until the UI
is ready to display them.

Design Doc:
https://docs.google.com/document/d/1MPvsrWiVD_jAC8ELyk8njFpy6j1thfVU5aWT3TCWE8w

Bug: 854329
Change-Id: I8442f588b0d7029062c67bbb2896cefeb558a887
Reviewed-on: https://chromium-review.googlesource.com/c/1297301
Commit-Queue: Ovidio Henriquez <odejesush@chromium.org>
Reviewed-by: Dave Schuyler <dschuyler@chromium.org>
Reviewed-by: Theresa <twellington@chromium.org>
Reviewed-by: Reilly Grant <reillyg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#609479}
diff --git a/chrome/browser/ui/webui/site_settings_helper.cc b/chrome/browser/ui/webui/site_settings_helper.cc
index 3777c1b9..98658e9 100644
--- a/chrome/browser/ui/webui/site_settings_helper.cc
+++ b/chrome/browser/ui/webui/site_settings_helper.cc
@@ -591,6 +591,12 @@
       chooser_context->GetAllGrantedObjects();
   AllOriginObjects all_origin_objects;
   for (const auto& object : objects) {
+    // Skip policy controlled objects until they are ready to be displayed.
+    // TODO(https://crbug.com/854329): Include policy controlled objects
+    // when the UI is capable of displaying them properly as policy controlled
+    // objects.
+    if (object->source == SiteSettingSourceToString(SiteSettingSource::kPolicy))
+      continue;
     std::string name = chooser_context->GetObjectName(object->object);
     // It is safe for this structure to hold references into |objects| because
     // they are both destroyed at the end of this function.
diff --git a/chrome/browser/usb/usb_chooser_context.cc b/chrome/browser/usb/usb_chooser_context.cc
index 0d05bc0..f594d75 100644
--- a/chrome/browser/usb/usb_chooser_context.cc
+++ b/chrome/browser/usb/usb_chooser_context.cc
@@ -12,20 +12,26 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/values.h"
+#include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/usb/usb_blocklist.h"
+#include "chrome/grit/generated_resources.h"
 #include "content/public/common/service_manager_connection.h"
 #include "device/usb/public/mojom/device.mojom.h"
+#include "device/usb/usb_ids.h"
 #include "services/device/public/mojom/constants.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
+#include "ui/base/l10n/l10n_util.h"
 
 namespace {
 
-const char kDeviceNameKey[] = "name";
-const char kGuidKey[] = "ephemeral-guid";
-const char kProductIdKey[] = "product-id";
-const char kSerialNumberKey[] = "serial-number";
-const char kVendorIdKey[] = "vendor-id";
+constexpr char kDeviceNameKey[] = "name";
+constexpr char kGuidKey[] = "ephemeral-guid";
+constexpr char kProductIdKey[] = "product-id";
+constexpr char kSerialNumberKey[] = "serial-number";
+constexpr char kVendorIdKey[] = "vendor-id";
+constexpr char kSourcePolicy[] = "policy";
+constexpr char kSourcePreference[] = "preference";
 
 // Reasons a permission may be closed. These are used in histograms so do not
 // remove/reorder entries. Only add at the end just before
@@ -49,6 +55,16 @@
   return !device_info.serial_number->empty();
 }
 
+std::pair<int, int> GetDeviceIds(const base::DictionaryValue& object) {
+  DCHECK(object.FindKeyOfType(kVendorIdKey, base::Value::Type::INTEGER));
+  int vendor_id = object.FindKey(kVendorIdKey)->GetInt();
+
+  DCHECK(object.FindKeyOfType(kProductIdKey, base::Value::Type::INTEGER));
+  int product_id = object.FindKey(kProductIdKey)->GetInt();
+
+  return std::make_pair(vendor_id, product_id);
+}
+
 std::unique_ptr<base::DictionaryValue> DeviceInfoToDictValue(
     const device::mojom::UsbDeviceInfo& device_info) {
   auto device_dict = std::make_unique<base::DictionaryValue>();
@@ -56,17 +72,56 @@
                       device_info.product_name
                           ? base::Value(*device_info.product_name)
                           : base::Value(""));
-
-  if (!CanStorePersistentEntry(device_info)) {
-    device_dict->SetKey(kGuidKey, base::Value(device_info.guid));
-    return device_dict;
-  }
   device_dict->SetKey(kVendorIdKey, base::Value(device_info.vendor_id));
   device_dict->SetKey(kProductIdKey, base::Value(device_info.product_id));
-  device_dict->SetKey(kSerialNumberKey,
-                      device_info.serial_number
-                          ? base::Value(*device_info.serial_number)
-                          : base::Value(""));
+
+  // CanStorePersistentEntry checks if |device_info.serial_number| is not empty.
+  if (CanStorePersistentEntry(device_info)) {
+    device_dict->SetKey(kSerialNumberKey,
+                        base::Value(*device_info.serial_number));
+  } else {
+    device_dict->SetKey(kGuidKey, base::Value(device_info.guid));
+  }
+
+  return device_dict;
+}
+
+base::string16 GetDeviceNameFromIds(int vendor_id, int product_id) {
+// This is currently using the UI strings used for the chooser prompt. This is
+// fine for now since the policy allowed devices are not being displayed in
+// Site Settings yet. However, policy allowed devices can contain wildcards for
+// the IDs, so more specific UI string need to be defined.
+// TODO(https://crbug.com/854329): Add UI strings that are more specific to
+// the Site Settings UI.
+#if !defined(OS_ANDROID)
+  const char* product_name =
+      device::UsbIds::GetProductName(vendor_id, product_id);
+  if (product_name)
+    return base::UTF8ToUTF16(product_name);
+
+  const char* vendor_name = device::UsbIds::GetVendorName(vendor_id);
+  if (vendor_name) {
+    return l10n_util::GetStringFUTF16(
+        IDS_DEVICE_CHOOSER_DEVICE_NAME_UNKNOWN_DEVICE_WITH_VENDOR_NAME,
+        base::UTF8ToUTF16(vendor_name));
+  }
+#endif  // !defined(OS_ANDROID)
+  return l10n_util::GetStringFUTF16(
+      IDS_DEVICE_CHOOSER_DEVICE_NAME_UNKNOWN_DEVICE_WITH_VENDOR_ID_AND_PRODUCT_ID,
+      base::ASCIIToUTF16(base::StringPrintf("%04x", vendor_id)),
+      base::ASCIIToUTF16(base::StringPrintf("%04x", product_id)));
+}
+
+std::unique_ptr<base::DictionaryValue> DeviceIdsToDictValue(int vendor_id,
+                                                            int product_id) {
+  auto device_dict = std::make_unique<base::DictionaryValue>();
+  base::string16 device_name = GetDeviceNameFromIds(vendor_id, product_id);
+
+  device_dict->SetKey(kDeviceNameKey, base::Value(device_name));
+  device_dict->SetKey(kVendorIdKey, base::Value(vendor_id));
+  device_dict->SetKey(kProductIdKey, base::Value(product_id));
+  device_dict->SetKey(kSerialNumberKey, base::Value(std::string()));
+
   return device_dict;
 }
 
@@ -190,7 +245,49 @@
       // ChooserContextBase::Object constructor will swap the object.
       auto object = DeviceInfoToDictValue(*devices_[guid]);
       objects.push_back(std::make_unique<ChooserContextBase::Object>(
-          requesting_origin, embedding_origin, object.get(), "preference",
+          requesting_origin, embedding_origin, object.get(), kSourcePreference,
+          is_incognito_));
+    }
+  }
+
+  // Iterate through the user granted objects to create a mapping of device IDs
+  // to device object for the policy granted objects to use, and remove
+  // objects that have already been granted permission by the policy.
+  std::map<std::pair<int, int>, base::Value> device_ids_to_object_map;
+  for (auto it = objects.begin(); it != objects.end();) {
+    const Object& object = **it;
+    auto device_ids = GetDeviceIds(object.object);
+    const GURL& requesting_origin = object.requesting_origin;
+    const GURL& embedding_origin = object.embedding_origin;
+
+    device_ids_to_object_map[device_ids] = object.object.Clone();
+
+    if (usb_policy_allowed_devices_->IsDeviceAllowed(
+            requesting_origin, embedding_origin, device_ids)) {
+      it = objects.erase(it);
+    } else {
+      ++it;
+    }
+  }
+
+  for (const auto& allowed_devices_entry : usb_policy_allowed_devices_->map()) {
+    // The map key is a tuple of (vendor_id, product_id).
+    const int vendor_id = allowed_devices_entry.first.first;
+    const int product_id = allowed_devices_entry.first.second;
+
+    for (const auto& url_pair : allowed_devices_entry.second) {
+      std::unique_ptr<base::DictionaryValue> object;
+      auto it =
+          device_ids_to_object_map.find(std::make_pair(vendor_id, product_id));
+      if (it != device_ids_to_object_map.end()) {
+        object = base::DictionaryValue::From(
+            base::Value::ToUniquePtrValue(it->second.Clone()));
+      } else {
+        object = DeviceIdsToDictValue(vendor_id, product_id);
+      }
+
+      objects.push_back(std::make_unique<ChooserContextBase::Object>(
+          url_pair.first, url_pair.second, object.get(), kSourcePolicy,
           is_incognito_));
     }
   }
@@ -324,7 +421,7 @@
 bool UsbChooserContext::IsValidObject(const base::DictionaryValue& object) {
   return object.size() == 4 && object.HasKey(kDeviceNameKey) &&
          object.HasKey(kVendorIdKey) && object.HasKey(kProductIdKey) &&
-         object.HasKey(kSerialNumberKey);
+         (object.HasKey(kSerialNumberKey) || object.HasKey(kGuidKey));
 }
 
 std::string UsbChooserContext::GetObjectName(
diff --git a/chrome/browser/usb/usb_chooser_context.h b/chrome/browser/usb/usb_chooser_context.h
index 9b9938a..3cce470 100644
--- a/chrome/browser/usb/usb_chooser_context.h
+++ b/chrome/browser/usb/usb_chooser_context.h
@@ -76,12 +76,12 @@
   void SetDeviceManagerForTesting(
       device::mojom::UsbDeviceManagerPtr fake_device_manager);
 
- private:
   // ChooserContextBase implementation.
   bool IsValidObject(const base::DictionaryValue& object) override;
   std::string GetObjectName(const base::DictionaryValue& object) override;
   void InitDeviceList(std::vector<::device::mojom::UsbDeviceInfoPtr> devices);
 
+ private:
   // device::mojom::UsbDeviceManagerClient implementation.
   void OnDeviceAdded(device::mojom::UsbDeviceInfoPtr device_info) override;
   void OnDeviceRemoved(device::mojom::UsbDeviceInfoPtr device_info) override;
diff --git a/chrome/browser/usb/usb_chooser_context_unittest.cc b/chrome/browser/usb/usb_chooser_context_unittest.cc
index 0d1a719..62f9a56 100644
--- a/chrome/browser/usb/usb_chooser_context_unittest.cc
+++ b/chrome/browser/usb/usb_chooser_context_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/bind_helpers.h"
 #include "base/json/json_reader.h"
 #include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/usb/usb_chooser_context.h"
 #include "chrome/browser/usb/usb_chooser_context_factory.h"
@@ -24,6 +25,13 @@
 
 namespace {
 
+constexpr char kDeviceNameKey[] = "name";
+constexpr char kGuidKey[] = "ephemeral-guid";
+constexpr char kProductIdKey[] = "product-id";
+constexpr char kSerialNumberKey[] = "serial-number";
+constexpr char kVendorIdKey[] = "vendor-id";
+constexpr int kDeviceIdWildcard = -1;
+
 class UsbChooserContextTest : public testing::Test {
  public:
   UsbChooserContextTest() {}
@@ -62,10 +70,10 @@
   UsbChooserContext* store = GetChooserContext(profile());
 
   base::DictionaryValue object_dict;
-  object_dict.SetString("name", "Gizmo");
-  object_dict.SetInteger("vendor-id", 0);
-  object_dict.SetInteger("product-id", 0);
-  object_dict.SetString("serial-number", "123ABC");
+  object_dict.SetString(kDeviceNameKey, "Gizmo");
+  object_dict.SetInteger(kVendorIdKey, 0);
+  object_dict.SetInteger(kProductIdKey, 0);
+  object_dict.SetString(kSerialNumberKey, "123ABC");
 
   EXPECT_FALSE(store->HasDevicePermission(origin, origin, *device_info));
   store->GrantDevicePermission(origin, origin, *device_info);
@@ -100,8 +108,10 @@
   UsbChooserContext* store = GetChooserContext(profile());
 
   base::DictionaryValue object_dict;
-  object_dict.SetString("name", "Gizmo");
-  object_dict.SetString("ephemeral-guid", device_info->guid);
+  object_dict.SetString(kDeviceNameKey, "Gizmo");
+  object_dict.SetString(kGuidKey, device_info->guid);
+  object_dict.SetInteger(kVendorIdKey, device_info->vendor_id);
+  object_dict.SetInteger(kProductIdKey, device_info->product_id);
 
   EXPECT_FALSE(store->HasDevicePermission(origin, origin, *device_info));
   store->GrantDevicePermission(origin, origin, *device_info);
@@ -292,16 +302,16 @@
 constexpr char kPolicySetting[] = R"(
     [
       {
-        "devices": [{ "vendor_id": 1234, "product_id": 5678 }],
+        "devices": [{ "vendor_id": 6353, "product_id": 5678 }],
         "urls": ["https://product.vendor.com"]
       }, {
-        "devices": [{ "vendor_id": 1234 }],
+        "devices": [{ "vendor_id": 6353 }],
         "urls": ["https://vendor.com"]
       }, {
         "devices": [{}],
         "urls": ["https://anydevice.com"]
       }, {
-        "devices": [{ "vendor_id": 2468, "product_id": 1357 }],
+        "devices": [{ "vendor_id": 6354, "product_id": 1357 }],
         "urls": ["https://gadget.com,https://cool.com"]
       }
     ])";
@@ -352,7 +362,7 @@
       GURL("https://gadget.com"), GURL("https://cool.com")};
 
   UsbDeviceInfoPtr specific_device_info = device_manager_.CreateAndAddDevice(
-      1234, 5678, "Google", "Gizmo", "ABC123");
+      6353, 5678, "Google", "Gizmo", "ABC123");
 
   auto* store = GetChooserContext(profile());
 
@@ -374,7 +384,7 @@
       GURL("https://cool.com")};
 
   UsbDeviceInfoPtr vendor_related_device_info =
-      device_manager_.CreateAndAddDevice(1234, 8765, "Google", "Widget",
+      device_manager_.CreateAndAddDevice(6353, 8765, "Google", "Widget",
                                          "XYZ987");
 
   auto* store = GetChooserContext(profile());
@@ -400,7 +410,7 @@
   const GURL& kCoolOrigin = kInvalidRequestingOrigins[2];
 
   UsbDeviceInfoPtr unrelated_device_info = device_manager_.CreateAndAddDevice(
-      2468, 1357, "Cool", "Gadget", "4W350M3");
+      6354, 1357, "Cool", "Gadget", "4W350M3");
 
   auto* store = GetChooserContext(profile());
 
@@ -428,9 +438,9 @@
   const GURL kCoolOrigin("https://cool.com");
 
   UsbDeviceInfoPtr specific_device_info = device_manager_.CreateAndAddDevice(
-      1234, 5678, "Google", "Gizmo", "ABC123");
+      6353, 5678, "Google", "Gizmo", "ABC123");
   UsbDeviceInfoPtr unrelated_device_info = device_manager_.CreateAndAddDevice(
-      2468, 1357, "Cool", "Gadget", "4W350M3");
+      6354, 1357, "Cool", "Gadget", "4W350M3");
 
   auto* store = GetChooserContext(profile());
 
@@ -465,3 +475,338 @@
   EXPECT_TRUE(store->HasDevicePermission(kGadgetOrigin, kCoolOrigin,
                                          *unrelated_device_info));
 }
+
+namespace {
+
+// Permission sources
+constexpr char kPolicySource[] = "policy";
+constexpr char kPreferenceSource[] = "preference";
+
+// Test URLs
+const GURL kAnyDeviceOrigin("https://anydevice.com");
+const GURL kVendorOrigin("https://vendor.com");
+const GURL kProductVendorOrigin("https://product.vendor.com");
+const GURL kGadgetOrigin("https://gadget.com");
+const GURL kCoolOrigin("https://cool.com");
+
+void ExpectDeviceObjectInfo(const base::DictionaryValue& actual,
+                            int vendor_id,
+                            int product_id,
+                            const std::string& name) {
+  const base::Value* vendor_id_value =
+      actual.FindKeyOfType(kVendorIdKey, base::Value::Type::INTEGER);
+  ASSERT_TRUE(vendor_id_value);
+  EXPECT_EQ(vendor_id_value->GetInt(), vendor_id);
+
+  const base::Value* product_id_value =
+      actual.FindKeyOfType(kProductIdKey, base::Value::Type::INTEGER);
+  ASSERT_TRUE(product_id_value);
+  EXPECT_EQ(product_id_value->GetInt(), product_id);
+
+  const base::Value* device_name_value =
+      actual.FindKeyOfType(kDeviceNameKey, base::Value::Type::STRING);
+  ASSERT_TRUE(device_name_value);
+  EXPECT_EQ(device_name_value->GetString(), name);
+}
+
+void ExpectChooserObjectInfo(const ChooserContextBase::Object* actual,
+                             const GURL& requesting_origin,
+                             const GURL& embedding_origin,
+                             const std::string& source,
+                             bool incognito,
+                             int vendor_id,
+                             int product_id,
+                             const std::string& name) {
+  ASSERT_TRUE(actual);
+  EXPECT_EQ(actual->requesting_origin, requesting_origin);
+  EXPECT_EQ(actual->embedding_origin, embedding_origin);
+  EXPECT_EQ(actual->source, source);
+  EXPECT_EQ(actual->incognito, incognito);
+  ExpectDeviceObjectInfo(actual->object, vendor_id, product_id, name);
+}
+
+void ExpectChooserObjectInfo(const ChooserContextBase::Object* actual,
+                             const GURL& requesting_origin,
+                             const std::string& source,
+                             bool incognito,
+                             int vendor_id,
+                             int product_id,
+                             const std::string& name) {
+  ExpectChooserObjectInfo(actual, requesting_origin, GURL::EmptyGURL(), source,
+                          incognito, vendor_id, product_id, name);
+}
+
+}  // namespace
+
+TEST_F(UsbChooserContextTest,
+       GetAllGrantedObjectsWithOnlyPolicyAllowedDevices) {
+  auto* store = GetChooserContext(profile());
+  profile()->GetPrefs()->Set(prefs::kManagedWebUsbAllowDevicesForUrls,
+                             *base::JSONReader::Read(kPolicySetting));
+
+  auto objects = store->GetAllGrantedObjects();
+  ASSERT_EQ(objects.size(), 4ul);
+
+  // The policy enforced objects that are returned by GetAllGrantedObjects() are
+  // ordered by the tuple (vendor_id, product_id) representing the device IDs.
+  // Wildcard IDs are represented by a value of -1, so they appear first.
+  ExpectChooserObjectInfo(objects[0].get(),
+                          /*requesting_origin=*/kAnyDeviceOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/kDeviceIdWildcard,
+                          /*product_id=*/kDeviceIdWildcard,
+                          /*name=*/"Unknown device [ffffffff:ffffffff]");
+  ExpectChooserObjectInfo(objects[1].get(),
+                          /*requesting_origin=*/kVendorOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/6353,
+                          /*product_id=*/kDeviceIdWildcard,
+                          /*name=*/"Unknown device from Google Inc.");
+  ExpectChooserObjectInfo(objects[2].get(),
+                          /*requesting_origin=*/kProductVendorOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/6353,
+                          /*product_id=*/5678,
+                          /*name=*/"Unknown device from Google Inc.");
+  ExpectChooserObjectInfo(objects[3].get(),
+                          /*requesting_origin=*/kGadgetOrigin,
+                          /*embedding_origin=*/kCoolOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/6354,
+                          /*product_id=*/1357,
+                          /*name=*/"Unknown device [18d2:054d]");
+}
+
+TEST_F(UsbChooserContextTest,
+       GetAllGrantedObjectsWithUserAndPolicyAllowedDevices) {
+  profile()->GetPrefs()->Set(prefs::kManagedWebUsbAllowDevicesForUrls,
+                             *base::JSONReader::Read(kPolicySetting));
+
+  const GURL kGoogleOrigin("https://www.google.com");
+  UsbDeviceInfoPtr persistent_device_info =
+      device_manager_.CreateAndAddDevice(1000, 1, "Google", "Gizmo", "123ABC");
+  UsbDeviceInfoPtr ephemeral_device_info =
+      device_manager_.CreateAndAddDevice(1000, 2, "Google", "Gadget", "");
+
+  auto* store = GetChooserContext(profile());
+  store->GrantDevicePermission(kGoogleOrigin, kGoogleOrigin,
+                               *persistent_device_info);
+  store->GrantDevicePermission(kGoogleOrigin, kGoogleOrigin,
+                               *ephemeral_device_info);
+
+  auto objects = store->GetAllGrantedObjects();
+  ASSERT_EQ(objects.size(), 6ul);
+
+  for (const auto& object : objects) {
+    EXPECT_TRUE(store->IsValidObject(object->object));
+  }
+
+  // The user granted permissions appear before the policy granted permissions.
+  // Within the user granted permissions, the persistent device permissions
+  // are added to the vector before ephemeral device permissions.
+  ExpectChooserObjectInfo(objects[0].get(),
+                          /*requesting_origin=*/kGoogleOrigin,
+                          /*embedding_origin=*/kGoogleOrigin,
+                          /*source=*/kPreferenceSource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/1000,
+                          /*product_id=*/1,
+                          /*name=*/"Gizmo");
+  ExpectChooserObjectInfo(objects[1].get(),
+                          /*requesting_origin=*/kGoogleOrigin,
+                          /*embedding_origin=*/kGoogleOrigin,
+                          /*source=*/kPreferenceSource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/1000,
+                          /*product_id=*/2,
+                          /*name=*/"Gadget");
+  ExpectChooserObjectInfo(objects[2].get(),
+                          /*requesting_origin=*/kAnyDeviceOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/kDeviceIdWildcard,
+                          /*product_id=*/kDeviceIdWildcard,
+                          /*name=*/"Unknown device [ffffffff:ffffffff]");
+  ExpectChooserObjectInfo(objects[3].get(),
+                          /*requesting_origin=*/kVendorOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/6353,
+                          /*product_id=*/kDeviceIdWildcard,
+                          /*name=*/"Unknown device from Google Inc.");
+  ExpectChooserObjectInfo(objects[4].get(),
+                          /*requesting_origin=*/kProductVendorOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/6353,
+                          /*product_id=*/5678,
+                          /*name=*/"Unknown device from Google Inc.");
+  ExpectChooserObjectInfo(objects[5].get(),
+                          /*requesting_origin=*/kGadgetOrigin,
+                          /*embedding_origin=*/kCoolOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/6354,
+                          /*product_id=*/1357,
+                          /*name=*/"Unknown device [18d2:054d]");
+}
+
+TEST_F(UsbChooserContextTest,
+       GetAllGrantedObjectsWithSpecificPolicyAndUserGrantedDevice) {
+  auto* store = GetChooserContext(profile());
+  profile()->GetPrefs()->Set(prefs::kManagedWebUsbAllowDevicesForUrls,
+                             *base::JSONReader::Read(kPolicySetting));
+
+  const GURL kProductVendorOrigin("https://product.vendor.com");
+  UsbDeviceInfoPtr persistent_device_info = device_manager_.CreateAndAddDevice(
+      6353, 5678, "Specific", "Product", "123ABC");
+
+  store->GrantDevicePermission(kProductVendorOrigin, kProductVendorOrigin,
+                               *persistent_device_info);
+
+  auto objects = store->GetAllGrantedObjects();
+  ASSERT_EQ(objects.size(), 4ul);
+
+  for (const auto& object : objects) {
+    EXPECT_TRUE(store->IsValidObject(object->object));
+  }
+
+  ExpectChooserObjectInfo(objects[0].get(),
+                          /*requesting_origin=*/kAnyDeviceOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/kDeviceIdWildcard,
+                          /*product_id=*/kDeviceIdWildcard,
+                          /*name=*/"Unknown device [ffffffff:ffffffff]");
+  ExpectChooserObjectInfo(objects[1].get(),
+                          /*requesting_origin=*/kVendorOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/6353,
+                          /*product_id=*/kDeviceIdWildcard,
+                          /*name=*/"Unknown device from Google Inc.");
+  ExpectChooserObjectInfo(objects[2].get(),
+                          /*requesting_origin=*/kProductVendorOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/6353,
+                          /*product_id=*/5678,
+                          /*name=*/"Product");
+  ExpectChooserObjectInfo(objects[3].get(),
+                          /*requesting_origin=*/kGadgetOrigin,
+                          /*embedding_origin=*/kCoolOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/6354,
+                          /*product_id=*/1357,
+                          /*name=*/"Unknown device [18d2:054d]");
+  ASSERT_TRUE(persistent_device_info->product_name);
+  EXPECT_EQ(base::UTF8ToUTF16(store->GetObjectName(objects[2]->object)),
+            persistent_device_info->product_name.value());
+}
+
+TEST_F(UsbChooserContextTest,
+       GetAllGrantedObjectsWithVendorPolicyAndUserGrantedDevice) {
+  const GURL kVendorOrigin("https://vendor.com");
+  UsbDeviceInfoPtr persistent_device_info = device_manager_.CreateAndAddDevice(
+      6353, 1000, "Vendor", "Product", "123ABC");
+
+  auto* store = GetChooserContext(profile());
+  profile()->GetPrefs()->Set(prefs::kManagedWebUsbAllowDevicesForUrls,
+                             *base::JSONReader::Read(kPolicySetting));
+
+  store->GrantDevicePermission(kVendorOrigin, kVendorOrigin,
+                               *persistent_device_info);
+
+  auto objects = store->GetAllGrantedObjects();
+  ASSERT_EQ(objects.size(), 4ul);
+
+  for (const auto& object : objects) {
+    EXPECT_TRUE(store->IsValidObject(object->object));
+  }
+
+  ExpectChooserObjectInfo(objects[0].get(),
+                          /*requesting_origin=*/kAnyDeviceOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/kDeviceIdWildcard,
+                          /*product_id=*/kDeviceIdWildcard,
+                          /*name=*/"Unknown device [ffffffff:ffffffff]");
+  ExpectChooserObjectInfo(objects[1].get(),
+                          /*requesting_origin=*/kVendorOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/6353,
+                          /*product_id=*/kDeviceIdWildcard,
+                          /*name=*/"Unknown device from Google Inc.");
+  ExpectChooserObjectInfo(objects[2].get(),
+                          /*requesting_origin=*/kProductVendorOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/6353,
+                          /*product_id=*/5678,
+                          /*name=*/"Unknown device from Google Inc.");
+  ExpectChooserObjectInfo(objects[3].get(),
+                          /*requesting_origin=*/kGadgetOrigin,
+                          /*embedding_origin=*/kCoolOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/6354,
+                          /*product_id=*/1357,
+                          /*name=*/"Unknown device [18d2:054d]");
+}
+
+TEST_F(UsbChooserContextTest,
+       GetAllGrantedObjectsWithAnyPolicyAndUserGrantedDevice) {
+  const GURL kAnyDeviceOrigin("https://anydevice.com");
+  UsbDeviceInfoPtr persistent_device_info = device_manager_.CreateAndAddDevice(
+      1123, 5813, "Some", "Product", "123ABC");
+
+  auto* store = GetChooserContext(profile());
+  profile()->GetPrefs()->Set(prefs::kManagedWebUsbAllowDevicesForUrls,
+                             *base::JSONReader::Read(kPolicySetting));
+
+  store->GrantDevicePermission(kAnyDeviceOrigin, kAnyDeviceOrigin,
+                               *persistent_device_info);
+
+  auto objects = store->GetAllGrantedObjects();
+  ASSERT_EQ(objects.size(), 4ul);
+
+  for (const auto& object : objects) {
+    EXPECT_TRUE(store->IsValidObject(object->object));
+  }
+
+  ExpectChooserObjectInfo(objects[0].get(),
+                          /*requesting_origin=*/kAnyDeviceOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/kDeviceIdWildcard,
+                          /*product_id=*/kDeviceIdWildcard,
+                          /*name=*/"Unknown device [ffffffff:ffffffff]");
+  ExpectChooserObjectInfo(objects[1].get(),
+                          /*requesting_origin=*/kVendorOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/6353,
+                          /*product_id=*/kDeviceIdWildcard,
+                          /*name=*/"Unknown device from Google Inc.");
+  ExpectChooserObjectInfo(objects[2].get(),
+                          /*requesting_origin=*/kProductVendorOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/6353,
+                          /*product_id=*/5678,
+                          /*name=*/"Unknown device from Google Inc.");
+  ExpectChooserObjectInfo(objects[3].get(),
+                          /*requesting_origin=*/kGadgetOrigin,
+                          /*embedding_origin=*/kCoolOrigin,
+                          /*source=*/kPolicySource,
+                          /*incognito=*/false,
+                          /*vendor_id=*/6354,
+                          /*product_id=*/1357,
+                          /*name=*/"Unknown device [18d2:054d]");
+}
diff --git a/chrome/browser/usb/usb_policy_allowed_devices.cc b/chrome/browser/usb/usb_policy_allowed_devices.cc
index c77ced02..8bbd5bb 100644
--- a/chrome/browser/usb/usb_policy_allowed_devices.cc
+++ b/chrome/browser/usb/usb_policy_allowed_devices.cc
@@ -59,14 +59,23 @@
     const GURL& requesting_origin,
     const GURL& embedding_origin,
     const device::mojom::UsbDeviceInfo& device_info) {
+  return IsDeviceAllowed(
+      requesting_origin, embedding_origin,
+      std::make_pair(device_info.vendor_id, device_info.product_id));
+}
+
+bool UsbPolicyAllowedDevices::IsDeviceAllowed(
+    const GURL& requesting_origin,
+    const GURL& embedding_origin,
+    const std::pair<int, int>& device_ids) {
   // Search through each set of URL pair that match the given device. The
   // keys correspond to the following URL pair sets:
   //  * (vendor_id, product_id): A set corresponding to the exact device.
   //  * (vendor_id, -1): A set corresponding to any device with |vendor_id|.
   //  * (-1, -1): A set corresponding to any device.
   const std::pair<int, int> set_keys[] = {
-      std::make_pair(device_info.vendor_id, device_info.product_id),
-      std::make_pair(device_info.vendor_id, -1), std::make_pair(-1, -1)};
+      std::make_pair(device_ids.first, device_ids.second),
+      std::make_pair(device_ids.first, -1), std::make_pair(-1, -1)};
 
   for (const auto& key : set_keys) {
     const auto entry = usb_device_ids_to_urls_.find(key);
diff --git a/chrome/browser/usb/usb_policy_allowed_devices.h b/chrome/browser/usb/usb_policy_allowed_devices.h
index 3d0cda8..ca1163b9 100644
--- a/chrome/browser/usb/usb_policy_allowed_devices.h
+++ b/chrome/browser/usb/usb_policy_allowed_devices.h
@@ -46,6 +46,9 @@
   bool IsDeviceAllowed(const GURL& requesting_origin,
                        const GURL& embedding_origin,
                        const device::mojom::UsbDeviceInfo& device_info);
+  bool IsDeviceAllowed(const GURL& requesting_origin,
+                       const GURL& embedding_origin,
+                       const std::pair<int, int>& device_ids);
 
   const UsbDeviceIdsToUrlsMap& map() const { return usb_device_ids_to_urls_; }