Commit 25065877 authored by Jesse Schettler's avatar Jesse Schettler Committed by Commit Bot

scanning: Get scanner capabilities

Update the LorgnetteScannerManager to get scanner capabilities using the
lorgnette D-Bus service.

Since a scanner can be detected by more than one SANE backend, the
LorgnetteScannerManager attemps to get capabilities using one available
backend at a time via the backend's corresponding device name. This is
repeated until one backend succeeds or they all fail.

Bug: b:165030685
Change-Id: I80d1550ca4215262ac4063f6bef4fbaf68ca6803
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2360227
Commit-Queue: Jesse Schettler <jschettler@chromium.org>
Reviewed-by: default avatarSteven Bennetts <stevenjb@chromium.org>
Reviewed-by: default avatarZentaro Kavanagh <zentaro@chromium.org>
Cr-Commit-Position: refs/heads/master@{#801531}
parent 54ca57b6
......@@ -23,12 +23,19 @@ void FakeLorgnetteScannerManager::GetScannerNames(
FROM_HERE, base::BindOnce(std::move(callback), scanner_names_));
}
void FakeLorgnetteScannerManager::GetScannerCapabilities(
const std::string& scanner_name,
GetScannerCapabilitiesCallback callback) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), scanner_capabilities_));
}
void FakeLorgnetteScannerManager::Scan(
const std::string& scanner_name,
const LorgnetteManagerClient::ScanProperties& scan_properties,
ScanCallback callback) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), std::move(scan_data_)));
FROM_HERE, base::BindOnce(std::move(callback), scan_data_));
}
void FakeLorgnetteScannerManager::SetGetScannerNamesResponse(
......@@ -36,6 +43,12 @@ void FakeLorgnetteScannerManager::SetGetScannerNamesResponse(
scanner_names_ = scanner_names;
}
void FakeLorgnetteScannerManager::SetGetScannerCapabilitiesResponse(
const base::Optional<lorgnette::ScannerCapabilities>&
scanner_capabilities) {
scanner_capabilities_ = scanner_capabilities;
}
void FakeLorgnetteScannerManager::SetScanResponse(
const base::Optional<std::string>& scan_data) {
scan_data_ = scan_data;
......
......@@ -10,6 +10,7 @@
#include "base/optional.h"
#include "chrome/browser/chromeos/scanning/lorgnette_scanner_manager.h"
#include "chromeos/dbus/lorgnette/lorgnette_service.pb.h"
#include "chromeos/dbus/lorgnette_manager_client.h"
namespace chromeos {
......@@ -25,6 +26,8 @@ class FakeLorgnetteScannerManager final : public LorgnetteScannerManager {
// LorgnetteScannerManager:
void GetScannerNames(GetScannerNamesCallback callback) override;
void GetScannerCapabilities(const std::string& scanner_name,
GetScannerCapabilitiesCallback callback) override;
void Scan(const std::string& scanner_name,
const LorgnetteManagerClient::ScanProperties& scan_properties,
ScanCallback callback) override;
......@@ -33,11 +36,17 @@ class FakeLorgnetteScannerManager final : public LorgnetteScannerManager {
void SetGetScannerNamesResponse(
const std::vector<std::string>& scanner_names);
// Sets the response returned by GetScannerCapabilities().
void SetGetScannerCapabilitiesResponse(
const base::Optional<lorgnette::ScannerCapabilities>&
scanner_capabilities);
// Sets the response returned by Scan().
void SetScanResponse(const base::Optional<std::string>& scan_data);
private:
std::vector<std::string> scanner_names_;
base::Optional<lorgnette::ScannerCapabilities> scanner_capabilities_;
base::Optional<std::string> scan_data_;
};
......
......@@ -4,6 +4,7 @@
#include "chrome/browser/chromeos/scanning/lorgnette_scanner_manager.h"
#include <array>
#include <memory>
#include <string>
#include <utility>
......@@ -28,6 +29,13 @@ namespace chromeos {
namespace {
// A prioritized list of scan protocols. Protocols that appear earlier in the
// list are preferred over those that appear later in the list when
// communicating with a connected scanner.
constexpr std::array<ScanProtocol, 4> kPrioritizedProtocols = {
ScanProtocol::kEscls, ScanProtocol::kEscl, ScanProtocol::kLegacyNetwork,
ScanProtocol::kLegacyUsb};
// Returns a pointer to LorgnetteManagerClient, which is used to detect and
// interact with scanners via the lorgnette D-Bus service.
LorgnetteManagerClient* GetLorgnetteManagerClient() {
......@@ -35,25 +43,6 @@ LorgnetteManagerClient* GetLorgnetteManagerClient() {
return DBusThreadManager::Get()->GetLorgnetteManagerClient();
}
// Returns the first usable device name corresponding to the highest priority
// protocol. Returns an empty string if no usable device name is found.
std::string GetUsableDeviceName(const Scanner& scanner) {
const std::vector<ScanProtocol> prioritized_protocols{
ScanProtocol::kEscls, ScanProtocol::kEscl, ScanProtocol::kLegacyNetwork,
ScanProtocol::kLegacyUsb};
for (const auto& protocol : prioritized_protocols) {
const auto it = scanner.device_names.find(protocol);
if (it != scanner.device_names.end()) {
for (const ScannerDeviceName& device_name : it->second) {
if (device_name.usable)
return device_name.device_name;
}
}
}
return "";
}
class LorgnetteScannerManagerImpl final : public LorgnetteScannerManager {
public:
LorgnetteScannerManagerImpl(
......@@ -79,19 +68,31 @@ class LorgnetteScannerManagerImpl final : public LorgnetteScannerManager {
}
// LorgnetteScannerManager:
void Scan(const std::string& scanner_name,
const LorgnetteManagerClient::ScanProperties& scan_properties,
ScanCallback callback) override {
const auto it = deduped_scanners_.find(scanner_name);
if (it == deduped_scanners_.end()) {
LOG(ERROR) << "Failed to find scanner with name " << scanner_name;
void GetScannerCapabilities(
const std::string& scanner_name,
GetScannerCapabilitiesCallback callback) override {
std::string device_name;
ScanProtocol protocol;
if (!GetUsableDeviceNameAndProtocol(scanner_name, device_name, protocol)) {
std::move(callback).Run(base::nullopt);
return;
}
const std::string device_name = GetUsableDeviceName(it->second);
if (device_name.empty()) {
LOG(ERROR) << "Failed to find usable device name for " << scanner_name;
GetLorgnetteManagerClient()->GetScannerCapabilities(
device_name,
base::BindOnce(
&LorgnetteScannerManagerImpl::OnScannerCapabilitiesResponse,
weak_ptr_factory_.GetWeakPtr(), std::move(callback), scanner_name,
device_name, protocol));
}
// LorgnetteScannerManager:
void Scan(const std::string& scanner_name,
const LorgnetteManagerClient::ScanProperties& scan_properties,
ScanCallback callback) override {
std::string device_name;
ScanProtocol protocol; // Unused.
if (!GetUsableDeviceNameAndProtocol(scanner_name, device_name, protocol)) {
std::move(callback).Run(base::nullopt);
return;
}
......@@ -125,6 +126,30 @@ class LorgnetteScannerManagerImpl final : public LorgnetteScannerManager {
std::move(callback).Run(std::move(scanner_names));
}
// Handles the result of calling
// LorgnetteManagerClient::GetScannerCapabilities(). If getting the scanner
// capabilities fails, |scanner_name|, |device_name|, and |protocol| are used
// to mark the device name that was used as unusable and retry the operation
// with the next available device name. This pattern of trying each device
// name cannot be used when performing a scan since the backend used to obtain
// the capabilities must be the same backend used to perform the scan.
void OnScannerCapabilitiesResponse(
GetScannerCapabilitiesCallback callback,
const std::string& scanner_name,
const std::string& device_name,
const ScanProtocol protocol,
base::Optional<lorgnette::ScannerCapabilities> capabilities) {
if (!capabilities) {
LOG(WARNING) << "Failed to get scanner capabilities using device name: "
<< device_name;
MarkDeviceNameUnusable(scanner_name, device_name, protocol);
GetScannerCapabilities(scanner_name, std::move(callback));
return;
}
std::move(callback).Run(capabilities);
}
// Handles the result of calling LorgnetteManagerClient::ScanImageToString().
void OnScanImageToStringResponse(ScanCallback callback,
base::Optional<std::string> scan_data) {
......@@ -208,6 +233,58 @@ class LorgnetteScannerManagerImpl final : public LorgnetteScannerManager {
return display_name;
}
// Gets the first usable device name corresponding to the highest priority
// protocol for the scanner specified by |scanner_name|. Returns true on
// success, false on failure.
bool GetUsableDeviceNameAndProtocol(const std::string& scanner_name,
std::string& device_name_out,
ScanProtocol& protocol_out) {
const auto scanner_it = deduped_scanners_.find(scanner_name);
if (scanner_it == deduped_scanners_.end()) {
LOG(ERROR) << "Failed to find scanner with name " << scanner_name;
return false;
}
for (const auto& protocol : kPrioritizedProtocols) {
const auto device_names_it =
scanner_it->second.device_names.find(protocol);
if (device_names_it == scanner_it->second.device_names.end())
continue;
for (const ScannerDeviceName& name : device_names_it->second) {
if (name.usable) {
device_name_out = name.device_name;
protocol_out = protocol;
return true;
}
}
}
LOG(ERROR) << "Failed to find usable device name for " << scanner_name;
return false;
}
// Marks a device name as unusable to prevent it from being returned by future
// calls to GetUsableDeviceNameAndProtocol().
void MarkDeviceNameUnusable(const std::string& scanner_name,
const std::string& device_name,
const ScanProtocol protocol) {
auto scanner_it = deduped_scanners_.find(scanner_name);
if (scanner_it == deduped_scanners_.end())
return;
auto device_names_it = scanner_it->second.device_names.find(protocol);
if (device_names_it == scanner_it->second.device_names.end())
return;
for (ScannerDeviceName& name : device_names_it->second) {
if (name.device_name == device_name) {
name.usable = false;
return;
}
}
}
// Used to detect zeroconf scanners.
std::unique_ptr<ZeroconfScannerDetector> zeroconf_scanner_detector_;
......
......@@ -11,6 +11,7 @@
#include "base/callback.h"
#include "base/optional.h"
#include "chromeos/dbus/lorgnette/lorgnette_service.pb.h"
#include "chromeos/dbus/lorgnette_manager_client.h"
#include "components/keyed_service/core/keyed_service.h"
......@@ -24,6 +25,8 @@ class LorgnetteScannerManager : public KeyedService {
public:
using GetScannerNamesCallback =
base::OnceCallback<void(std::vector<std::string> scanner_names)>;
using GetScannerCapabilitiesCallback = base::OnceCallback<void(
base::Optional<lorgnette::ScannerCapabilities> capabilities)>;
using ScanCallback =
base::OnceCallback<void(base::Optional<std::string> scan_data)>;
......@@ -35,6 +38,13 @@ class LorgnetteScannerManager : public KeyedService {
// Returns the names of all available, deduplicated scanners.
virtual void GetScannerNames(GetScannerNamesCallback callback) = 0;
// Returns the capabilities of the scanner specified by |scanner_name|. If
// |scanner_name| does not correspond to a known scanner, base::nullopt is
// returned in the callback.
virtual void GetScannerCapabilities(
const std::string& scanner_name,
GetScannerCapabilitiesCallback callback) = 0;
// Performs a scan with the scanner specified by |scanner_name| using the
// given |scan_properties|. If |scanner_name| does not correspond to a known
// scanner, base::nullopt is returned in the callback.
......
......@@ -149,6 +149,15 @@ class LorgnetteScannerManagerTest : public testing::Test {
base::Unretained(this)));
}
// Calls LorgnetteScannerManager::GetScannerCapabilities() and binds a
// callback to process the result.
void GetScannerCapabilities(const std::string& scanner_name) {
lorgnette_scanner_manager_->GetScannerCapabilities(
scanner_name,
base::Bind(&LorgnetteScannerManagerTest::GetScannerCapabilitiesCallback,
base::Unretained(this)));
}
// Calls LorgnetteScannerManager::Scan() and binds a callback to process the
// result.
void Scan(const std::string& scanner_name,
......@@ -176,6 +185,10 @@ class LorgnetteScannerManagerTest : public testing::Test {
return scanner_names_;
}
base::Optional<lorgnette::ScannerCapabilities> scanner_capabilities() const {
return scanner_capabilities_;
}
base::Optional<std::string> scan_data() const { return scan_data_; }
private:
......@@ -185,6 +198,12 @@ class LorgnetteScannerManagerTest : public testing::Test {
run_loop_->Quit();
}
void GetScannerCapabilitiesCallback(
base::Optional<lorgnette::ScannerCapabilities> scanner_capabilities) {
scanner_capabilities_ = scanner_capabilities;
run_loop_->Quit();
}
// Handles the result of calling LorgnetteScannerManager::Scan().
void ScanCallback(base::Optional<std::string> scan_data) {
scan_data_ = scan_data;
......@@ -200,6 +219,7 @@ class LorgnetteScannerManagerTest : public testing::Test {
std::unique_ptr<LorgnetteScannerManager> lorgnette_scanner_manager_;
std::vector<std::string> scanner_names_;
base::Optional<lorgnette::ScannerCapabilities> scanner_capabilities_;
base::Optional<std::string> scan_data_;
};
......@@ -304,6 +324,73 @@ TEST_F(LorgnetteScannerManagerTest, RemoveScanner) {
EXPECT_TRUE(scanner_names().empty());
}
// Test that getting capabilities fails when GetScannerNames() has never been
// called.
TEST_F(LorgnetteScannerManagerTest, GetCapsNoScanner) {
GetScannerCapabilities(kUnknownScannerName);
WaitForResult();
EXPECT_FALSE(scanner_capabilities());
}
// Test that getting capabilities fails when the scanner name does not
// correspond to a known scanner.
TEST_F(LorgnetteScannerManagerTest, GetCapsUnknownScanner) {
fake_zeroconf_scanner_detector()->AddDetections({CreateZeroconfScanner()});
CompleteTasks();
GetScannerNames();
WaitForResult();
GetScannerCapabilities(kUnknownScannerName);
WaitForResult();
EXPECT_FALSE(scanner_capabilities());
}
// Test that getting capabilities fails when there is no usable device name.
TEST_F(LorgnetteScannerManagerTest, GetCapsNoUsableDeviceName) {
auto scanner = CreateZeroconfScanner(/*usable=*/false);
fake_zeroconf_scanner_detector()->AddDetections({scanner});
CompleteTasks();
GetScannerNames();
WaitForResult();
GetScannerCapabilities(scanner.display_name);
WaitForResult();
EXPECT_FALSE(scanner_capabilities());
}
// Test that failing to get capabilities from lorgnette returns no capabilities.
TEST_F(LorgnetteScannerManagerTest, GetCapsFail) {
auto scanner = CreateZeroconfScanner();
fake_zeroconf_scanner_detector()->AddDetections({scanner});
CompleteTasks();
GetScannerNames();
WaitForResult();
GetLorgnetteManagerClient()->SetScannerCapabilitiesResponse(base::nullopt);
GetScannerCapabilities(scanner.display_name);
WaitForResult();
EXPECT_FALSE(scanner_capabilities());
}
// Test that getting capabilities succeeds with a valid scanner name.
TEST_F(LorgnetteScannerManagerTest, GetCaps) {
auto scanner = CreateZeroconfScanner();
fake_zeroconf_scanner_detector()->AddDetections({scanner});
CompleteTasks();
GetScannerNames();
WaitForResult();
lorgnette::ScannerCapabilities capabilities;
capabilities.add_resolutions(300);
capabilities.add_color_modes(lorgnette::MODE_COLOR);
GetLorgnetteManagerClient()->SetScannerCapabilitiesResponse(capabilities);
GetScannerCapabilities(scanner.display_name);
WaitForResult();
ASSERT_TRUE(scanner_capabilities());
const auto caps = scanner_capabilities().value();
ASSERT_EQ(caps.resolutions_size(), 1);
EXPECT_EQ(caps.resolutions()[0], 300u);
EXPECT_EQ(caps.sources_size(), 0);
ASSERT_EQ(caps.color_modes_size(), 1);
EXPECT_EQ(caps.color_modes()[0], lorgnette::MODE_COLOR);
}
// Test that scanning fails when GetScannerNames() has never been called.
TEST_F(LorgnetteScannerManagerTest, NoScannersNames) {
chromeos::LorgnetteManagerClient::ScanProperties properties;
......
......@@ -26,6 +26,13 @@ void FakeLorgnetteManagerClient::ListScanners(
FROM_HERE, base::BindOnce(std::move(callback), list_scanners_response_));
}
void FakeLorgnetteManagerClient::GetScannerCapabilities(
const std::string& device_name,
DBusMethodCallback<lorgnette::ScannerCapabilities> callback) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), capabilities_response_));
}
void FakeLorgnetteManagerClient::StartScan(
std::string device_name,
const ScanProperties& properties,
......@@ -50,6 +57,12 @@ void FakeLorgnetteManagerClient::SetListScannersResponse(
list_scanners_response_ = list_scanners_response;
}
void FakeLorgnetteManagerClient::SetScannerCapabilitiesResponse(
const base::Optional<lorgnette::ScannerCapabilities>&
capabilities_response) {
capabilities_response_ = capabilities_response;
}
void FakeLorgnetteManagerClient::SetScanResponse(
const base::Optional<std::string>& scan_image_response) {
scan_image_response_ = scan_image_response;
......
......@@ -26,7 +26,9 @@ class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeLorgnetteManagerClient
void ListScanners(
DBusMethodCallback<lorgnette::ListScannersResponse> callback) override;
void GetScannerCapabilities(
const std::string& device_name,
DBusMethodCallback<lorgnette::ScannerCapabilities> callback) override;
void StartScan(std::string device_name,
const ScanProperties& properties,
DBusMethodCallback<std::string> completion_callback,
......@@ -38,11 +40,17 @@ class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeLorgnetteManagerClient
const base::Optional<lorgnette::ListScannersResponse>&
list_scanners_response);
// Sets the response returned by GetScannerCapabilities().
void SetScannerCapabilitiesResponse(
const base::Optional<lorgnette::ScannerCapabilities>&
capabilities_response);
// Sets the response returned by ScanImageToString() and StartScan().
void SetScanResponse(const base::Optional<std::string>& scan_image_response);
private:
base::Optional<lorgnette::ListScannersResponse> list_scanners_response_;
base::Optional<lorgnette::ScannerCapabilities> capabilities_response_;
base::Optional<std::string> scan_image_response_;
};
......
......@@ -50,6 +50,20 @@ class LorgnetteManagerClientImpl : public LorgnetteManagerClient {
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void GetScannerCapabilities(
const std::string& device_name,
DBusMethodCallback<lorgnette::ScannerCapabilities> callback) override {
dbus::MethodCall method_call(lorgnette::kManagerServiceInterface,
lorgnette::kGetScannerCapabilitiesMethod);
dbus::MessageWriter writer(&method_call);
writer.AppendString(device_name);
lorgnette_daemon_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(
&LorgnetteManagerClientImpl::OnScannerCapabilitiesResponse,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
// LorgnetteManagerClient override.
void StartScan(std::string device_name,
const ScanProperties& properties,
......@@ -209,6 +223,27 @@ class LorgnetteManagerClientImpl : public LorgnetteManagerClient {
std::move(callback).Run(std::move(response_proto));
}
// Handles the response received after calling GetScannerCapabilities().
void OnScannerCapabilitiesResponse(
DBusMethodCallback<lorgnette::ScannerCapabilities> callback,
dbus::Response* response) {
if (!response) {
LOG(ERROR) << "Failed to obtain ScannerCapabilities";
std::move(callback).Run(base::nullopt);
return;
}
lorgnette::ScannerCapabilities response_proto;
dbus::MessageReader reader(response);
if (!reader.PopArrayOfBytesAsProto(&response_proto)) {
LOG(ERROR) << "Failed to read ScannerCapabilities";
std::move(callback).Run(base::nullopt);
return;
}
std::move(callback).Run(std::move(response_proto));
}
// Called when scan data read is completed.
// This is to maintain the lifetime of ScanDataReader instance.
void OnScanDataCompleted(DBusMethodCallback<std::string> callback,
......
......@@ -36,6 +36,12 @@ class COMPONENT_EXPORT(CHROMEOS_DBUS) LorgnetteManagerClient
virtual void ListScanners(
DBusMethodCallback<lorgnette::ListScannersResponse> callback) = 0;
// Gets the capabilities of the scanner corresponding to |device_name| and
// returns them using the provided |callback|.
virtual void GetScannerCapabilities(
const std::string& device_name,
DBusMethodCallback<lorgnette::ScannerCapabilities> callback) = 0;
// Request a scanned image using lorgnette's StartScan API and calls
// |completion_callback| when completed with a string pointing at the scanned
// image data. Image data will be stored in the .png format.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment