Reland: Adding IEEE1284 USB-Printer Device ID
For some USB-printers we need some information from the IEEE 1284 Device
ID to resolve PPD matching. This change now queries and saves this ID
for any USB-discovered printers.
This reverts commit 77625ed97f17bf1b9b8eb33927a90d5725236a18.
Revert reason fixed by https://chromium-review.googlesource.com/c/chromium/src/+/1681340
Originally reviewed as https://chromium-review.googlesource.com/c/chromium/src/+/1661117
Bug: chromium:895037, chromium:926022
Test: manually tested
Change-Id: I67cf48eb1a62c316e35d28c7e8ab0f61f3901365
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1681361
Commit-Queue: Sean Kau <[email protected]>
Reviewed-by: Steven Bennetts <[email protected]>
Cr-Commit-Position: refs/heads/master@{#674170}
diff --git a/chrome/browser/chromeos/printing/usb_printer_detector.cc b/chrome/browser/chromeos/printing/usb_printer_detector.cc
index 0e5de53..5c3a75d4 100644
--- a/chrome/browser/chromeos/printing/usb_printer_detector.cc
+++ b/chrome/browser/chromeos/printing/usb_printer_detector.cc
@@ -27,11 +27,13 @@
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/debug_daemon_client.h"
#include "chromeos/printing/ppd_provider.h"
+#include "chromeos/printing/usb_printer_id.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/system_connector.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
#include "services/device/public/mojom/constants.mojom.h"
+#include "services/device/public/mojom/usb_device.mojom.h"
#include "services/device/public/mojom/usb_manager_client.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
@@ -128,9 +130,24 @@
GuessEffectiveMakeAndModel(device_info));
entry.ppd_search_data.discovery_type =
PrinterSearchData::PrinterDiscoveryType::kUsb;
- // TODO(https://crbug.com/895037): Add in command set from IEEE1284
- printers_[device_info.guid] = entry;
+ // Query printer for an IEEE Device ID.
+ device::mojom::UsbDevicePtr device_ptr;
+ device_manager_->GetDevice(device_info.guid, mojo::MakeRequest(&device_ptr),
+ nullptr /* device_client */);
+ GetDeviceId(std::move(device_ptr),
+ base::BindOnce(&UsbPrinterDetectorImpl::OnGetDeviceId,
+ weak_factory_.GetWeakPtr(), std::move(entry),
+ device_info.guid));
+ }
+
+ void OnGetDeviceId(DetectedPrinter entry,
+ std::string guid,
+ UsbPrinterId printer_id) {
+ entry.ppd_search_data.printer_id = std::move(printer_id);
+
+ // Add detected printer.
+ printers_[guid] = entry;
if (on_printers_found_callback_) {
on_printers_found_callback_.Run(GetPrinters());
}
diff --git a/chrome/browser/chromeos/printing/usb_printer_util.cc b/chrome/browser/chromeos/printing/usb_printer_util.cc
index fe57a93..e486a02 100644
--- a/chrome/browser/chromeos/printing/usb_printer_util.cc
+++ b/chrome/browser/chromeos/printing/usb_printer_util.cc
@@ -7,10 +7,13 @@
#include <ctype.h>
#include <stdint.h>
+#include <algorithm>
#include <string>
+#include <utility>
#include <vector>
#include "base/big_endian.h"
+#include "base/bind_helpers.h"
#include "base/hash/md5.h"
#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
@@ -19,7 +22,9 @@
#include "base/strings/utf_string_conversions.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/printing/printer_configuration.h"
+#include "chromeos/printing/usb_printer_id.h"
#include "services/device/public/cpp/usb/usb_utils.h"
+#include "services/device/public/mojom/usb_device.mojom.h"
#include "services/device/public/mojom/usb_enumeration_options.mojom.h"
#include "services/device/public/mojom/usb_manager.mojom.h"
#include "ui/base/l10n/l10n_util.h"
@@ -41,6 +46,69 @@
// (http://www.usb.org/developers/docs/devclass_docs/IPP.zip).
constexpr uint8_t kPrinterIppusbProtocol = 4;
+// Configuration for a GET_DEVICE_ID Printer Class-Specific Request.
+const int kGetDeviceIdRequest = 0;
+const int kDefaultInterface = 0;
+const int kDefaultConfiguration = 0;
+
+// Callback for device.mojom.UsbDevice.ControlTransferIn.
+// Expects |data| to hold a newly queried Device ID.
+void OnControlTransfer(device::mojom::UsbDevicePtr device_ptr,
+ GetDeviceIdCallback cb,
+ device::mojom::UsbTransferStatus status,
+ const std::vector<uint8_t>& data) {
+ if (status != device::mojom::UsbTransferStatus::COMPLETED || data.empty()) {
+ return std::move(cb).Run({});
+ }
+
+ // Cleanup device_ptr.
+ device_ptr->ReleaseInterface(kDefaultInterface, base::DoNothing());
+ device_ptr->Close(base::DoNothing());
+
+ return std::move(cb).Run(UsbPrinterId(data));
+}
+
+// Callback for device.mojom.UsbDevice.ClaimInterface.
+// If interface was claimed successfully, attempts to query printer for a
+// Device ID.
+void OnClaimInterface(device::mojom::UsbDevicePtr device_ptr,
+ GetDeviceIdCallback cb,
+ bool success) {
+ if (!success) {
+ return std::move(cb).Run({});
+ }
+
+ auto params = device::mojom::UsbControlTransferParams::New();
+ params->type = device::mojom::UsbControlTransferType::CLASS;
+ params->recipient = device::mojom::UsbControlTransferRecipient::INTERFACE;
+ params->request = kGetDeviceIdRequest;
+ params->value = kDefaultConfiguration; // default config index
+ params->index = kDefaultInterface; // default interface index
+
+ // Query for IEEE1284 string.
+ auto* device = device_ptr.get();
+ device->ControlTransferIn(
+ std::move(params), 255 /* max size */, 2000 /* 2 second timeout */,
+ base::BindOnce(OnControlTransfer, std::move(device_ptr), std::move(cb)));
+}
+
+// Callback for device.mojom.UsbDevice.Open.
+// If device was opened successfully, attempts to claim printer's default
+// interface.
+void OnDeviceOpen(device::mojom::UsbDevicePtr device_ptr,
+ GetDeviceIdCallback cb,
+ device::mojom::UsbOpenDeviceError error) {
+ if (error != device::mojom::UsbOpenDeviceError::OK || !device_ptr) {
+ return std::move(cb).Run({});
+ }
+
+ // Claim interface.
+ auto* device = device_ptr.get();
+ device->ClaimInterface(
+ kDefaultInterface,
+ base::BindOnce(OnClaimInterface, std::move(device_ptr), std::move(cb)));
+}
+
// Escape URI strings the same way cups does it, so we end up with a URI cups
// recognizes. Cups hex-encodes '%', ' ', and anything not in the standard
// ASCII range. CUPS lets everything else through unchanged.
@@ -108,7 +176,7 @@
// possible for that device. So we basically toss every bit of stable
// information from the device into an MD5 hash, and then hexify the hash value
// as a suffix to "usb-" as the final printer id.
-std::string UsbPrinterId(const UsbDeviceInfo& device_info) {
+std::string CreateUsbPrinterId(const UsbDeviceInfo& device_info) {
// Paranoid checks; in the unlikely event someone messes with the USB device
// definition, our (supposedly stable) hashes will change.
static_assert(sizeof(device_info.class_code) == 1, "Class size changed");
@@ -258,9 +326,17 @@
printer->set_description(printer->display_name());
printer->set_uri(UsbPrinterUri(device_info));
- printer->set_id(UsbPrinterId(device_info));
+ printer->set_id(CreateUsbPrinterId(device_info));
printer->set_supports_ippusb(UsbDeviceSupportsIppusb(device_info));
return printer;
}
+void GetDeviceId(device::mojom::UsbDevicePtr device_ptr,
+ GetDeviceIdCallback cb) {
+ // Open device.
+ auto* device = device_ptr.get();
+ device->Open(
+ base::BindOnce(OnDeviceOpen, std::move(device_ptr), std::move(cb)));
+}
+
} // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/usb_printer_util.h b/chrome/browser/chromeos/printing/usb_printer_util.h
index 76066b5..f05d8ce 100644
--- a/chrome/browser/chromeos/printing/usb_printer_util.h
+++ b/chrome/browser/chromeos/printing/usb_printer_util.h
@@ -14,6 +14,7 @@
namespace chromeos {
class Printer;
+class UsbPrinterId;
base::string16 GetManufacturerName(
const device::mojom::UsbDeviceInfo& device_info);
@@ -33,6 +34,12 @@
std::unique_ptr<Printer> UsbDeviceToPrinter(
const device::mojom::UsbDeviceInfo& device_info);
+// Expects |device_ptr| to be linked to a Printer-class USB Device. Queries the
+// printer for its IEEE 1284 Standard Device ID.
+using GetDeviceIdCallback = base::OnceCallback<void(UsbPrinterId)>;
+void GetDeviceId(device::mojom::UsbDevicePtr device_ptr,
+ GetDeviceIdCallback cb);
+
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_PRINTING_USB_PRINTER_UTIL_H__
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn
index 18369f10..d4947c69 100644
--- a/chromeos/BUILD.gn
+++ b/chromeos/BUILD.gn
@@ -62,6 +62,8 @@
"printing/printer_translator.h",
"printing/uri_components.cc",
"printing/uri_components.h",
+ "printing/usb_printer_id.cc",
+ "printing/usb_printer_id.h",
"process_proxy/process_output_watcher.cc",
"process_proxy/process_output_watcher.h",
"process_proxy/process_proxy.cc",
@@ -182,6 +184,7 @@
"printing/ppd_provider_unittest.cc",
"printing/printer_configuration_unittest.cc",
"printing/printer_translator_unittest.cc",
+ "printing/usb_printer_id_unittest.cc",
"process_proxy/process_output_watcher_unittest.cc",
"process_proxy/process_proxy_unittest.cc",
"test/run_all_unittests.cc",
diff --git a/chromeos/printing/epson_driver_matching.cc b/chromeos/printing/epson_driver_matching.cc
index d25b5a6..8f1bd00d 100644
--- a/chromeos/printing/epson_driver_matching.cc
+++ b/chromeos/printing/epson_driver_matching.cc
@@ -34,7 +34,7 @@
"application/octet-stream");
case PrinterSearchData::PrinterDiscoveryType::kUsb:
- return base::Contains(sd.usb_command_set, "ESC/P-R");
+ return base::Contains(sd.printer_id.command_set(), "ESC/P-R");
case PrinterSearchData::PrinterDiscoveryType::kZeroconf:
// For printers found through mDNS/DNS-SD discovery,
diff --git a/chromeos/printing/epson_driver_matching_unittest.cc b/chromeos/printing/epson_driver_matching_unittest.cc
index 42d274a..8bf627a 100644
--- a/chromeos/printing/epson_driver_matching_unittest.cc
+++ b/chromeos/printing/epson_driver_matching_unittest.cc
@@ -5,6 +5,7 @@
#include "chromeos/printing/epson_driver_matching.h"
#include <string>
+#include <vector>
#include "chromeos/printing/ppd_provider.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -31,7 +32,7 @@
case PrinterDiscoveryType::kUsb:
sd.discovery_type = PrinterDiscoveryType::kUsb;
- sd.usb_command_set.push_back(kEscPr);
+ sd.printer_id.set_command_set({kEscPr});
break;
case PrinterDiscoveryType::kZeroconf:
@@ -96,7 +97,7 @@
sd.supported_document_formats.push_back(std::string(kOctetStream) + "afds");
EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
- sd.usb_command_set.push_back(kOctetStream);
+ sd.printer_id.set_command_set({kOctetStream});
EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
sd.supported_document_formats.push_back(kOctetStream);
@@ -106,18 +107,21 @@
// Simple PrinterDiscoveryType::kUsb checks.
TEST(EpsonDriverMatchingTest, UsbDiscovery) {
PrinterSearchData sd(GetTestPrinterSearchData(PrinterDiscoveryType::kUsb));
- sd.usb_command_set.clear();
+ std::vector<std::string> command_set;
- sd.usb_command_set.push_back("ESC");
+ command_set.push_back("ESC");
+ sd.printer_id.set_command_set(command_set);
EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
- sd.usb_command_set.push_back(std::string(kEscPr) + ":asfd");
+ command_set.push_back(std::string(kEscPr) + ":asfd");
+ sd.printer_id.set_command_set(command_set);
EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
sd.supported_document_formats.push_back(kEscPr);
EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
- sd.usb_command_set.push_back(kEscPr);
+ command_set.push_back(kEscPr);
+ sd.printer_id.set_command_set(command_set);
EXPECT_TRUE(CanUseEpsonGenericPPD(sd));
}
@@ -132,7 +136,7 @@
sd.supported_document_formats.push_back(std::string(kEpsonEscpr) + ":asfd");
EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
- sd.usb_command_set.push_back(kEpsonEscpr);
+ sd.printer_id.set_command_set({kEpsonEscpr});
EXPECT_FALSE(CanUseEpsonGenericPPD(sd));
sd.supported_document_formats.push_back(kEpsonEscpr);
diff --git a/chromeos/printing/ppd_provider.h b/chromeos/printing/ppd_provider.h
index 51baa82a..b5110b03 100644
--- a/chromeos/printing/ppd_provider.h
+++ b/chromeos/printing/ppd_provider.h
@@ -16,6 +16,7 @@
#include "base/version.h"
#include "chromeos/chromeos_export.h"
#include "chromeos/printing/printer_configuration.h"
+#include "chromeos/printing/usb_printer_id.h"
namespace network {
namespace mojom {
@@ -58,9 +59,9 @@
// Set of MIME types supported by this printer.
std::vector<std::string> supported_document_formats;
- // Stripped from IEEE1284 signaling method(from the device ID key 'CMD').
- // Details a set of languages this printer understands.
- std::vector<std::string> usb_command_set;
+ // Representation of IEEE1284 standard printing device ID.
+ // Contains a set of languages this printer understands.
+ UsbPrinterId printer_id;
};
// PpdProvider is responsible for mapping printer descriptions to
diff --git a/chromeos/printing/usb_printer_id.cc b/chromeos/printing/usb_printer_id.cc
new file mode 100644
index 0000000..5c7a81e
--- /dev/null
+++ b/chromeos/printing/usb_printer_id.cc
@@ -0,0 +1,88 @@
+// Copyright 2019 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 "chromeos/printing/usb_printer_id.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/stl_util.h"
+#include "base/strings/string_split.h"
+
+namespace chromeos {
+
+// Device ID keys pulled from IEEE Standard 1284.
+const char kManufacturer[] = "MANUFACTURER";
+const char kManufacturerAbbr[] = "MFG";
+const char kModel[] = "MODEL";
+const char kModelAbbr[] = "MDL";
+const char kCommandSet[] = "COMMAND SET";
+const char kCommandSetAbbr[] = "CMD";
+
+UsbPrinterId::UsbPrinterId(const std::vector<uint8_t>& device_id_data) {
+ // Build mapping.
+ id_mappings_ = BuildDeviceIdMapping(device_id_data);
+
+ // Save required mappings.
+ // Save make_.
+ if (base::Contains(id_mappings_, kManufacturer)) {
+ make_ = id_mappings_[kManufacturer].front();
+ } else if (base::Contains(id_mappings_, kManufacturerAbbr)) {
+ make_ = id_mappings_[kManufacturerAbbr].front();
+ }
+
+ // Save model_.
+ if (base::Contains(id_mappings_, kModel)) {
+ model_ = id_mappings_[kModel].front();
+ } else if (base::Contains(id_mappings_, kModelAbbr)) {
+ model_ = id_mappings_[kModelAbbr].front();
+ }
+
+ // Save command_set_.
+ if (base::Contains(id_mappings_, kCommandSet)) {
+ command_set_ = id_mappings_[kCommandSet];
+ } else if (base::Contains(id_mappings_, kCommandSetAbbr)) {
+ command_set_ = id_mappings_[kCommandSetAbbr];
+ }
+}
+
+UsbPrinterId::UsbPrinterId() = default;
+UsbPrinterId::UsbPrinterId(const UsbPrinterId& other) = default;
+UsbPrinterId::~UsbPrinterId() = default;
+
+std::map<std::string, std::vector<std::string>> BuildDeviceIdMapping(
+ const std::vector<uint8_t>& data) {
+ // Must contain at least the length information.
+ if (data.size() < 2) {
+ return {};
+ }
+
+ std::map<std::string, std::vector<std::string>> ret;
+
+ // Convert to string to work on.
+ // Note: First two bytes contain the length, so we skip those.
+ std::string printer_id;
+ std::copy(data.begin() + 2, data.end(), std::back_inserter(printer_id));
+
+ // We filter out terms with empty keys or values.
+ base::StringPairs terms;
+ base::SplitStringIntoKeyValuePairs(printer_id, ':', ';', &terms);
+ for (const auto& term : terms) {
+ if (term.first.empty()) {
+ continue;
+ }
+
+ std::vector<std::string> values = base::SplitString(
+ term.second, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ if (values.empty()) {
+ continue;
+ }
+
+ ret[term.first] = values;
+ }
+
+ return ret;
+}
+
+} // namespace chromeos
diff --git a/chromeos/printing/usb_printer_id.h b/chromeos/printing/usb_printer_id.h
new file mode 100644
index 0000000..3df4257
--- /dev/null
+++ b/chromeos/printing/usb_printer_id.h
@@ -0,0 +1,60 @@
+// Copyright 2019 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.
+
+#ifndef CHROMEOS_PRINTING_USB_PRINTER_ID_H_
+#define CHROMEOS_PRINTING_USB_PRINTER_ID_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "chromeos/chromeos_export.h"
+
+namespace chromeos {
+
+// This class parses and holds the IEEE 1284 Device ID string as queried
+// from a USB-connected printer.
+class CHROMEOS_EXPORT UsbPrinterId {
+ public:
+ UsbPrinterId();
+ UsbPrinterId(const UsbPrinterId& other);
+ ~UsbPrinterId();
+
+ // Expects |printer_id_data| to contain the data portion response to a USB
+ // Printer Class-Specific GET_DEVICE_ID Request.
+ explicit UsbPrinterId(const std::vector<uint8_t>& printer_id_data);
+
+ // Accessors.
+ std::string make() const { return make_; }
+ std::string model() const { return model_; }
+ std::vector<std::string> command_set() const { return command_set_; }
+
+ // Setters (only used in testing).
+ void set_make(std::string make) { make_ = make; }
+ void set_model(std::string model) { model_ = model; }
+ void set_command_set(std::vector<std::string> command_set) {
+ command_set_ = std::move(command_set);
+ }
+
+ private:
+ std::string make_;
+ std::string model_;
+
+ // List of supported document formats (MIME types).
+ std::vector<std::string> command_set_;
+
+ // Holds the fully parsed IEEE 1284 Device ID.
+ std::map<std::string, std::vector<std::string>> id_mappings_;
+};
+
+// Expects data to hold a IEEE 1284 Device ID. Parses |data| and returns the
+// resulting key-value(s) pairs.
+CHROMEOS_EXPORT std::map<std::string, std::vector<std::string>>
+BuildDeviceIdMapping(const std::vector<uint8_t>& data);
+
+} // namespace chromeos
+
+#endif // CHROMEOS_PRINTING_USB_PRINTER_ID_H_
diff --git a/chromeos/printing/usb_printer_id_unittest.cc b/chromeos/printing/usb_printer_id_unittest.cc
new file mode 100644
index 0000000..7ad21a1
--- /dev/null
+++ b/chromeos/printing/usb_printer_id_unittest.cc
@@ -0,0 +1,74 @@
+// Copyright 2019 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 "chromeos/printing/usb_printer_id.h"
+
+#include <algorithm>
+#include <map>
+#include <string>
+
+#include "base/strings/string_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+namespace {
+
+using testing::IsEmpty;
+
+using MapType = std::map<std::string, std::vector<std::string>>;
+
+MapType GetDefaultDeviceId() {
+ MapType ret;
+
+ // Make.
+ ret["MFG"].push_back("EPSON");
+
+ // Model.
+ ret["MDL"].push_back("ET-2700");
+
+ // Command set.
+ ret["CMD"].push_back("ESCPL2");
+ ret["CMD"].push_back("BDC");
+ ret["CMD"].push_back("D4");
+ ret["CMD"].push_back("END4");
+ ret["CMD"].push_back("GENEP");
+
+ return ret;
+}
+
+std::string MapToString(const MapType& map) {
+ std::vector<std::string> terms;
+ for (auto& term : map) {
+ std::string values = base::JoinString(term.second, ",");
+ terms.push_back(base::JoinString({term.first, values}, ":"));
+ }
+
+ std::string device_id_str = "xx"; // Two unused bytes for the length.
+ device_id_str += base::JoinString(terms, ";") + ";";
+ return device_id_str;
+}
+
+std::vector<uint8_t> MapToBuffer(const MapType& map) {
+ std::string device_id_str = MapToString(map);
+
+ std::vector<uint8_t> ret;
+ std::copy(device_id_str.begin(), device_id_str.end(),
+ std::back_inserter(ret));
+ return ret;
+}
+
+TEST(UsbPrinterIdTest, EmptyDeviceId) {
+ EXPECT_THAT(BuildDeviceIdMapping({}), IsEmpty());
+}
+
+// Tests that we get the same map back after parsing.
+TEST(UsbPrinterIdTest, SimpleSanityTest) {
+ MapType mapping = GetDefaultDeviceId();
+ std::vector<uint8_t> buffer = MapToBuffer(mapping);
+ EXPECT_EQ(mapping, BuildDeviceIdMapping(buffer));
+}
+
+} // namespace
+} // namespace chromeos