Commit 76152eea authored by Jesse Schettler's avatar Jesse Schettler Committed by Commit Bot

scanning: Create LorgnetteScannerManager

Create the LorgnetteScannerManager to manage all available scanners in
Chrome OS. Clients can request the names of available scanners and use
them to perform scans.

The manager obtains available scanners using a ZeroconfScannerDetector
and the lorgnette D-Bus service. It deduplicates the scanners before
giving their names to clients and then uses the lorgnette D-Bus service
to perform scans.

Bug: b:153541027
Change-Id: I4e93ee7a97e3c06ec8b6390db79997c1441eac7f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2318310Reviewed-by: default avatarZentaro Kavanagh <zentaro@chromium.org>
Commit-Queue: Jesse Schettler <jschettler@chromium.org>
Cr-Commit-Position: refs/heads/master@{#797901}
parent 95555558
...@@ -2419,6 +2419,12 @@ source_set("chromeos") { ...@@ -2419,6 +2419,12 @@ source_set("chromeos") {
"remote_apps/remote_apps_model.cc", "remote_apps/remote_apps_model.cc",
"remote_apps/remote_apps_model.h", "remote_apps/remote_apps_model.h",
"reset/metrics.h", "reset/metrics.h",
"scanning/lorgnette_scanner_manager.cc",
"scanning/lorgnette_scanner_manager.h",
"scanning/lorgnette_scanner_manager_factory.cc",
"scanning/lorgnette_scanner_manager_factory.h",
"scanning/lorgnette_scanner_manager_util.cc",
"scanning/lorgnette_scanner_manager_util.h",
"scanning/scanner_detector.h", "scanning/scanner_detector.h",
"scanning/zeroconf_scanner_detector.cc", "scanning/zeroconf_scanner_detector.cc",
"scanning/zeroconf_scanner_detector.h", "scanning/zeroconf_scanner_detector.h",
...@@ -3465,6 +3471,8 @@ source_set("unit_tests") { ...@@ -3465,6 +3471,8 @@ source_set("unit_tests") {
"release_notes/release_notes_notification_unittest.cc", "release_notes/release_notes_notification_unittest.cc",
"release_notes/release_notes_storage_unittest.cc", "release_notes/release_notes_storage_unittest.cc",
"remote_apps/remote_apps_model_unittest.cc", "remote_apps/remote_apps_model_unittest.cc",
"scanning/lorgnette_scanner_manager_unittest.cc",
"scanning/lorgnette_scanner_manager_util_unittest.cc",
"scanning/zeroconf_scanner_detector_unittest.cc", "scanning/zeroconf_scanner_detector_unittest.cc",
"scheduler_configuration_manager_unittest.cc", "scheduler_configuration_manager_unittest.cc",
"session_length_limiter_unittest.cc", "session_length_limiter_unittest.cc",
......
// Copyright 2020 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 "chrome/browser/chromeos/scanning/lorgnette_scanner_manager.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/check.h"
#include "base/containers/flat_map.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/chromeos/scanning/lorgnette_scanner_manager_util.h"
#include "chrome/browser/chromeos/scanning/zeroconf_scanner_detector.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/lorgnette/lorgnette_service.pb.h"
#include "chromeos/dbus/lorgnette_manager_client.h"
#include "chromeos/scanning/scanner.h"
#include "net/base/ip_address.h"
namespace chromeos {
namespace {
// Returns a pointer to LorgnetteManagerClient, which is used to detect and
// interact with scanners via the lorgnette D-Bus service.
LorgnetteManagerClient* GetLorgnetteManagerClient() {
DCHECK(DBusThreadManager::IsInitialized());
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(
std::unique_ptr<ZeroconfScannerDetector> zeroconf_scanner_detector)
: zeroconf_scanner_detector_(std::move(zeroconf_scanner_detector)) {
zeroconf_scanner_detector_->RegisterScannersDetectedCallback(
base::BindRepeating(&LorgnetteScannerManagerImpl::OnScannersDetected,
weak_ptr_factory_.GetWeakPtr()));
OnScannersDetected(zeroconf_scanner_detector_->GetScanners());
}
~LorgnetteScannerManagerImpl() override = default;
// KeyedService:
void Shutdown() override { weak_ptr_factory_.InvalidateWeakPtrs(); }
// LorgnetteScannerManager:
void GetScannerNames(GetScannerNamesCallback callback) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_);
GetLorgnetteManagerClient()->ListScanners(
base::BindOnce(&LorgnetteScannerManagerImpl::OnListScannersResponse,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
// 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;
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;
std::move(callback).Run(base::nullopt);
return;
}
GetLorgnetteManagerClient()->StartScan(
device_name, scan_properties,
base::BindOnce(
&LorgnetteScannerManagerImpl::OnScanImageToStringResponse,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
base::nullopt);
}
private:
// Called when scanners are detected by a ScannerDetector.
void OnScannersDetected(std::vector<Scanner> scanners) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_);
zeroconf_scanners_ = scanners;
}
// Handles the result of calling LorgnetteManagerClient::ListScanners().
void OnListScannersResponse(
GetScannerNamesCallback callback,
base::Optional<lorgnette::ListScannersResponse> response) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_);
RebuildDedupedScanners(response);
std::vector<std::string> scanner_names;
scanner_names.reserve(deduped_scanners_.size());
for (const auto& entry : deduped_scanners_)
scanner_names.push_back(entry.first);
std::move(callback).Run(std::move(scanner_names));
}
// Handles the result of calling LorgnetteManagerClient::ScanImageToString().
void OnScanImageToStringResponse(ScanCallback callback,
base::Optional<std::string> scan_data) {
std::move(callback).Run(scan_data);
}
// Uses |response| and zeroconf_scanners_ to rebuild deduped_scanners_.
void RebuildDedupedScanners(
base::Optional<lorgnette::ListScannersResponse> response) {
ResetDedupedScanners();
if (!response || response->scanners_size() == 0)
return;
// Iterate through each lorgnette scanner and add its info to an existing
// Scanner if it has a matching IP address. Otherwise, create a new Scanner
// for the lorgnette scanner.
base::flat_map<net::IPAddress, Scanner*> known_ip_addresses =
GetKnownIpAddresses();
for (const auto& lorgnette_scanner : response->scanners()) {
std::string ip_address_str;
ScanProtocol protocol = ScanProtocol::kUnknown;
ParseScannerName(lorgnette_scanner.name(), ip_address_str, protocol);
if (!ip_address_str.empty()) {
net::IPAddress ip_address;
if (ip_address.AssignFromIPLiteral(ip_address_str)) {
const auto it = known_ip_addresses.find(ip_address);
if (it != known_ip_addresses.end()) {
it->second->device_names[protocol].emplace(
lorgnette_scanner.name());
continue;
}
}
}
const bool is_usb_scanner = protocol == ScanProtocol::kLegacyUsb;
const std::string base_name = base::StringPrintf(
"%s %s%s", lorgnette_scanner.manufacturer().c_str(),
lorgnette_scanner.model().c_str(), is_usb_scanner ? " (USB)" : "");
const std::string display_name = CreateUniqueDisplayName(base_name);
Scanner scanner;
scanner.display_name = display_name;
scanner.device_names[protocol].emplace(lorgnette_scanner.name());
deduped_scanners_[display_name] = scanner;
}
}
// Resets |deduped_scanners_| by clearing it and repopulating it with
// zeroconf_scanners_.
void ResetDedupedScanners() {
deduped_scanners_.clear();
deduped_scanners_.reserve(zeroconf_scanners_.size());
for (const auto& scanner : zeroconf_scanners_)
deduped_scanners_[scanner.display_name] = scanner;
}
// Returns a map of IP addresses to the scanners they correspond to in
// deduped_scanners_. This enables deduplication of network scanners by making
// it easy to check for and modify them using their IP addresses.
base::flat_map<net::IPAddress, Scanner*> GetKnownIpAddresses() {
base::flat_map<net::IPAddress, Scanner*> known_ip_addresses;
for (auto& entry : deduped_scanners_) {
for (const auto& ip_address : entry.second.ip_addresses)
known_ip_addresses[ip_address] = &entry.second;
}
return known_ip_addresses;
}
// Creates a unique display name by appending a copy number to a duplicate
// name (e.g. if Scanner Name already exists, the second instance will be
// renamed Scanner Name (1)).
std::string CreateUniqueDisplayName(const std::string& base_name) {
std::string display_name = base_name;
int i = 1; // The first duplicate will become "Scanner Name (1)."
while (deduped_scanners_.find(display_name) != deduped_scanners_.end()) {
display_name = base::StringPrintf("%s (%d)", base_name.c_str(), i);
i++;
}
return display_name;
}
// Used to detect zeroconf scanners.
std::unique_ptr<ZeroconfScannerDetector> zeroconf_scanner_detector_;
// The deduplicated zeroconf scanners reported by the
// zeroconf_scanner_detector_.
std::vector<Scanner> zeroconf_scanners_;
// Stores the deduplicated scanners from all sources in a map of display name
// to Scanner. Clients are given display names and can use them to interact
// with the corresponding scanners.
base::flat_map<std::string, Scanner> deduped_scanners_;
SEQUENCE_CHECKER(sequence_);
base::WeakPtrFactory<LorgnetteScannerManagerImpl> weak_ptr_factory_{this};
};
} // namespace
// static
std::unique_ptr<LorgnetteScannerManager> LorgnetteScannerManager::Create(
std::unique_ptr<ZeroconfScannerDetector> zeroconf_scanner_detector) {
return std::make_unique<LorgnetteScannerManagerImpl>(
std::move(zeroconf_scanner_detector));
}
} // namespace chromeos
// Copyright 2020 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 CHROME_BROWSER_CHROMEOS_SCANNING_LORGNETTE_SCANNER_MANAGER_H_
#define CHROME_BROWSER_CHROMEOS_SCANNING_LORGNETTE_SCANNER_MANAGER_H_
#include <memory>
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/optional.h"
#include "chromeos/dbus/lorgnette_manager_client.h"
#include "components/keyed_service/core/keyed_service.h"
namespace chromeos {
class ZeroconfScannerDetector;
// Top-level manager of available scanners in Chrome OS. All functions in this
// class must be called from a sequenced context.
class LorgnetteScannerManager : public KeyedService {
public:
using GetScannerNamesCallback =
base::OnceCallback<void(std::vector<std::string> scanner_names)>;
using ScanCallback =
base::OnceCallback<void(base::Optional<std::string> scan_data)>;
~LorgnetteScannerManager() override = default;
static std::unique_ptr<LorgnetteScannerManager> Create(
std::unique_ptr<ZeroconfScannerDetector> zeroconf_scanner_detector);
// Returns the names of all available, deduplicated scanners.
virtual void GetScannerNames(GetScannerNamesCallback 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.
virtual void Scan(
const std::string& scanner_name,
const LorgnetteManagerClient::ScanProperties& scan_properties,
ScanCallback callback) = 0;
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_SCANNING_LORGNETTE_SCANNER_MANAGER_H_
// Copyright 2020 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 "chrome/browser/chromeos/scanning/lorgnette_scanner_manager_factory.h"
#include "base/memory/singleton.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/chromeos/scanning/lorgnette_scanner_manager.h"
#include "chrome/browser/chromeos/scanning/zeroconf_scanner_detector.h"
#include "chrome/browser/profiles/profile.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "content/public/browser/browser_context.h"
namespace chromeos {
// static
LorgnetteScannerManager* LorgnetteScannerManagerFactory::GetForBrowserContext(
content::BrowserContext* context) {
return static_cast<LorgnetteScannerManager*>(
LorgnetteScannerManagerFactory::GetInstance()
->GetServiceForBrowserContext(context, /*create=*/true));
}
// static
LorgnetteScannerManagerFactory* LorgnetteScannerManagerFactory::GetInstance() {
return base::Singleton<LorgnetteScannerManagerFactory>::get();
}
LorgnetteScannerManagerFactory::LorgnetteScannerManagerFactory()
: BrowserContextKeyedServiceFactory(
"LorgnetteScannerManager",
BrowserContextDependencyManager::GetInstance()) {}
LorgnetteScannerManagerFactory::~LorgnetteScannerManagerFactory() = default;
KeyedService* LorgnetteScannerManagerFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
// Prevent an instance of LorgnetteScannerManager from being created on the
// lock screen.
Profile* profile = Profile::FromBrowserContext(context);
if (ProfileHelper::IsLockScreenAppProfile(profile) ||
ProfileHelper::IsSigninProfile(profile)) {
return nullptr;
}
return LorgnetteScannerManager::Create(ZeroconfScannerDetector::Create())
.release();
}
bool LorgnetteScannerManagerFactory::ServiceIsCreatedWithBrowserContext()
const {
return true;
}
bool LorgnetteScannerManagerFactory::ServiceIsNULLWhileTesting() const {
return true;
}
} // namespace chromeos
// Copyright 2020 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 CHROME_BROWSER_CHROMEOS_SCANNING_LORGNETTE_SCANNER_MANAGER_FACTORY_H_
#define CHROME_BROWSER_CHROMEOS_SCANNING_LORGNETTE_SCANNER_MANAGER_FACTORY_H_
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
namespace base {
template <typename T>
struct DefaultSingletonTraits;
}
namespace content {
class BrowserContext;
}
namespace chromeos {
class LorgnetteScannerManager;
// Factory for LorgnetteScannerManager.
class LorgnetteScannerManagerFactory
: public BrowserContextKeyedServiceFactory {
public:
static LorgnetteScannerManager* GetForBrowserContext(
content::BrowserContext* context);
static LorgnetteScannerManagerFactory* GetInstance();
private:
friend struct base::DefaultSingletonTraits<LorgnetteScannerManagerFactory>;
LorgnetteScannerManagerFactory();
~LorgnetteScannerManagerFactory() override;
LorgnetteScannerManagerFactory(const LorgnetteScannerManagerFactory&) =
delete;
LorgnetteScannerManagerFactory& operator=(
const LorgnetteScannerManagerFactory&) = delete;
// BrowserContextKeyedServiceFactory:
KeyedService* BuildServiceInstanceFor(
content::BrowserContext* context) const override;
bool ServiceIsCreatedWithBrowserContext() const override;
bool ServiceIsNULLWhileTesting() const override;
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_SCANNING_LORGNETTE_SCANNER_MANAGER_FACTORY_H_
// Copyright 2020 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 "chrome/browser/chromeos/scanning/lorgnette_scanner_manager.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/containers/flat_map.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/task_environment.h"
#include "chrome/browser/chromeos/scanning/zeroconf_scanner_detector.h"
#include "chrome/browser/chromeos/scanning/zeroconf_scanner_detector_utils.h"
#include "chrome/browser/local_discovery/service_discovery_client.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_lorgnette_manager_client.h"
#include "chromeos/dbus/lorgnette/lorgnette_service.pb.h"
#include "chromeos/scanning/scanner.h"
#include "net/base/ip_address.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace {
using local_discovery::ServiceDescription;
using ::testing::ElementsAreArray;
// Test device names for different types of lorgnette scanners.
constexpr char kLorgnetteNetworkIpDeviceName[] = "test:MX3100_192.168.0.3";
constexpr char kLorgnetteNetworkUrlDeviceName[] =
"http://testscanner.domain.org";
constexpr char kLorgnetteUsbDeviceName[] = "test:04A91752_94370B";
// A scanner name that does not correspond to a known scanner.
constexpr char kUnknownScannerName[] = "Unknown Scanner";
// Returns a ScannerInfo object with the given |name|.
lorgnette::ScannerInfo CreateLorgnetteScanner(std::string name) {
lorgnette::ScannerInfo scanner;
scanner.set_name(name);
scanner.set_manufacturer("Test");
scanner.set_model("MX3100");
scanner.set_type("Flatbed");
return scanner;
}
// Returns a ListScannersResponse containing a single ScannerInfo object created
// with the given |name|.
lorgnette::ListScannersResponse CreateListScannersResponse(std::string name) {
lorgnette::ScannerInfo scanner = CreateLorgnetteScanner(name);
lorgnette::ListScannersResponse response;
*response.add_scanners() = std::move(scanner);
return response;
}
// Returns a zeroconf Scanner with the device name marked as |usable|.
Scanner CreateZeroconfScanner(bool usable = true) {
return CreateSaneAirscanScanner("Test MX3100",
ZeroconfScannerDetector::kEsclsServiceType,
"", net::IPAddress(192, 168, 0, 3), 5, usable)
.value();
}
class FakeZeroconfScannerDetector final : public ZeroconfScannerDetector {
public:
FakeZeroconfScannerDetector() = default;
~FakeZeroconfScannerDetector() override = default;
void RegisterScannersDetectedCallback(
OnScannersDetectedCallback callback) override {
on_scanners_detected_callback_ = std::move(callback);
}
std::vector<Scanner> GetScanners() override {
std::vector<Scanner> scanners;
for (const auto& entry : scanners_)
scanners.push_back(entry.second);
return scanners;
}
void OnDeviceChanged(const std::string& service_type,
bool added,
const ServiceDescription& service_description) override {
}
void OnDeviceRemoved(const std::string& service_type,
const std::string& service_name) override {}
void OnDeviceCacheFlushed(const std::string& service_type) override {}
// Used to trigger on_scanners_detected_callback_ after adding the given
// |scanners| to the detected scanners.
void AddDetections(const std::vector<Scanner>& scanners) {
for (const auto& scanner : scanners)
scanners_[scanner.display_name] = scanner;
on_scanners_detected_callback_.Run(GetScanners());
}
// Used to trigger on_scanners_detected_callback_ after removing the given
// |scanners| from the detected scanners.
void RemoveDetections(const std::vector<Scanner>& scanners) {
for (const auto& scanner : scanners)
scanners_.erase(scanner.display_name);
on_scanners_detected_callback_.Run(GetScanners());
}
private:
base::flat_map<std::string, Scanner> scanners_;
OnScannersDetectedCallback on_scanners_detected_callback_;
};
} // namespace
class LorgnetteScannerManagerTest : public testing::Test {
public:
LorgnetteScannerManagerTest() {
run_loop_ = std::make_unique<base::RunLoop>();
DBusThreadManager::Initialize();
auto fake_zeroconf_scanner_detector =
std::make_unique<FakeZeroconfScannerDetector>();
fake_zeroconf_scanner_detector_ = fake_zeroconf_scanner_detector.get();
lorgnette_scanner_manager_ = LorgnetteScannerManager::Create(
std::move(fake_zeroconf_scanner_detector));
}
~LorgnetteScannerManagerTest() override { DBusThreadManager::Shutdown(); }
FakeLorgnetteManagerClient* GetLorgnetteManagerClient() {
return static_cast<FakeLorgnetteManagerClient*>(
DBusThreadManager::Get()->GetLorgnetteManagerClient());
}
// Calls LorgnetteScannerManager::GetScannerNames() and binds a callback to
// process the result.
void GetScannerNames() {
lorgnette_scanner_manager_->GetScannerNames(
base::Bind(&LorgnetteScannerManagerTest::GetScannerNamesCallback,
base::Unretained(this)));
}
// Calls LorgnetteScannerManager::Scan() and binds a callback to process the
// result.
void Scan(const std::string& scanner_name,
const LorgnetteManagerClient::ScanProperties& scan_properties) {
lorgnette_scanner_manager_->Scan(
scanner_name, scan_properties,
base::Bind(&LorgnetteScannerManagerTest::ScanCallback,
base::Unretained(this)));
}
// Runs all tasks until the ThreadPool's non-delayed queues are empty.
void CompleteTasks() { task_environment_.RunUntilIdle(); }
// Runs run_loop_ until a callback calls Quit().
void WaitForResult() {
run_loop_->Run();
run_loop_.reset(new base::RunLoop());
}
FakeZeroconfScannerDetector* fake_zeroconf_scanner_detector() {
return fake_zeroconf_scanner_detector_;
}
const std::vector<std::string>& scanner_names() const {
return scanner_names_;
}
base::Optional<std::string> scan_data() const { return scan_data_; }
private:
// Handles the result of calling LorgnetteScannerManager::GetScannerNames().
void GetScannerNamesCallback(std::vector<std::string> scanner_names) {
scanner_names_ = scanner_names;
run_loop_->Quit();
}
// Handles the result of calling LorgnetteScannerManager::Scan().
void ScanCallback(base::Optional<std::string> scan_data) {
scan_data_ = scan_data;
run_loop_->Quit();
}
base::test::TaskEnvironment task_environment_;
std::unique_ptr<base::RunLoop> run_loop_;
FakeZeroconfScannerDetector* fake_zeroconf_scanner_detector_;
std::unique_ptr<LorgnetteScannerManager> lorgnette_scanner_manager_;
std::vector<std::string> scanner_names_;
base::Optional<std::string> scan_data_;
};
// Test that no scanner names are returned when no scanners have been detected.
TEST_F(LorgnetteScannerManagerTest, NoScanners) {
GetScannerNames();
WaitForResult();
EXPECT_TRUE(scanner_names().empty());
}
// Test that the name of a detected zeroconf scanner can be retrieved.
TEST_F(LorgnetteScannerManagerTest, ZeroconfScanner) {
auto scanner = CreateZeroconfScanner();
fake_zeroconf_scanner_detector()->AddDetections({scanner});
CompleteTasks();
GetScannerNames();
WaitForResult();
EXPECT_THAT(scanner_names(), ElementsAreArray({scanner.display_name}));
}
// Test that the name of a detected lorgnette scanner can be retrieved.
TEST_F(LorgnetteScannerManagerTest, LorgnetteScanner) {
lorgnette::ListScannersResponse response =
CreateListScannersResponse(kLorgnetteNetworkIpDeviceName);
GetLorgnetteManagerClient()->SetListScannersResponse(response);
GetScannerNames();
WaitForResult();
const auto& scanner = response.scanners()[0];
std::string scanner_name = scanner.manufacturer() + " " + scanner.model();
EXPECT_THAT(scanner_names(), ElementsAreArray({scanner_name}));
}
// Test that two detected scanners with the same IP address are deduplicated and
// reported with single scanner name.
TEST_F(LorgnetteScannerManagerTest, DeduplicateScanner) {
GetLorgnetteManagerClient()->SetListScannersResponse(
CreateListScannersResponse(kLorgnetteNetworkIpDeviceName));
auto scanner = CreateZeroconfScanner();
fake_zeroconf_scanner_detector()->AddDetections({scanner});
CompleteTasks();
GetScannerNames();
WaitForResult();
EXPECT_THAT(scanner_names(), ElementsAreArray({scanner.display_name}));
}
// Test that a lorgnette scanner with a URL in the name gets reported as a
// network scanner instead of a USB scanner (i.e. USB is not in the returned
// scanner name).
TEST_F(LorgnetteScannerManagerTest, LorgnetteScannerWithUrl) {
lorgnette::ListScannersResponse response =
CreateListScannersResponse(kLorgnetteNetworkUrlDeviceName);
GetLorgnetteManagerClient()->SetListScannersResponse(response);
GetScannerNames();
WaitForResult();
auto& scanner = response.scanners()[0];
std::string scanner_name = scanner.manufacturer() + " " + scanner.model();
EXPECT_THAT(scanner_names(), ElementsAreArray({scanner_name}));
}
// Test that detecting a lorgnette USB scanner results in a scanner name ending
// with "(USB)."
TEST_F(LorgnetteScannerManagerTest, LorgnetteUSBScanner) {
lorgnette::ListScannersResponse response =
CreateListScannersResponse(kLorgnetteUsbDeviceName);
GetLorgnetteManagerClient()->SetListScannersResponse(response);
GetScannerNames();
WaitForResult();
auto& scanner = response.scanners()[0];
std::string scanner_name =
scanner.manufacturer() + " " + scanner.model() + " (USB)";
EXPECT_THAT(scanner_names(), ElementsAreArray({scanner_name}));
}
// Test that two lorgnette scanners with the same manufacturer and model are
// given unique names.
TEST_F(LorgnetteScannerManagerTest, UniqueScannerNames) {
lorgnette::ListScannersResponse response =
CreateListScannersResponse(kLorgnetteNetworkIpDeviceName);
lorgnette::ScannerInfo scanner =
CreateLorgnetteScanner(kLorgnetteNetworkIpDeviceName);
*response.add_scanners() = std::move(scanner);
GetLorgnetteManagerClient()->SetListScannersResponse(response);
GetScannerNames();
WaitForResult();
ASSERT_EQ(scanner_names().size(), 2ul);
EXPECT_NE(scanner_names()[0], scanner_names()[1]);
}
// Test that removing a detected scanner removes it from the list of available
// scanners.
TEST_F(LorgnetteScannerManagerTest, RemoveScanner) {
auto scanner = CreateZeroconfScanner();
fake_zeroconf_scanner_detector()->AddDetections({scanner});
CompleteTasks();
GetScannerNames();
WaitForResult();
EXPECT_THAT(scanner_names(), ElementsAreArray({scanner.display_name}));
fake_zeroconf_scanner_detector()->RemoveDetections({scanner});
CompleteTasks();
GetScannerNames();
WaitForResult();
EXPECT_TRUE(scanner_names().empty());
}
// Test that scanning fails when GetScannerNames() has never been called.
TEST_F(LorgnetteScannerManagerTest, NoScannersNames) {
chromeos::LorgnetteManagerClient::ScanProperties properties;
Scan(kUnknownScannerName, properties);
WaitForResult();
EXPECT_FALSE(scan_data());
}
// Test that scanning fails when the scanner name does not correspond to a known
// scanner.
TEST_F(LorgnetteScannerManagerTest, UnknownScannerName) {
fake_zeroconf_scanner_detector()->AddDetections({CreateZeroconfScanner()});
CompleteTasks();
GetScannerNames();
WaitForResult();
chromeos::LorgnetteManagerClient::ScanProperties properties;
Scan(kUnknownScannerName, properties);
WaitForResult();
EXPECT_FALSE(scan_data());
}
// Test that scanning fails when there is no usable device name.
TEST_F(LorgnetteScannerManagerTest, NoUsableDeviceName) {
auto scanner = CreateZeroconfScanner(/*usable=*/false);
fake_zeroconf_scanner_detector()->AddDetections({scanner});
CompleteTasks();
GetScannerNames();
WaitForResult();
chromeos::LorgnetteManagerClient::ScanProperties properties;
Scan(scanner.display_name, properties);
WaitForResult();
EXPECT_FALSE(scan_data());
}
// Test that scanning succeeds with a valid scanner name.
TEST_F(LorgnetteScannerManagerTest, Scan) {
auto scanner = CreateZeroconfScanner();
fake_zeroconf_scanner_detector()->AddDetections({scanner});
CompleteTasks();
GetScannerNames();
WaitForResult();
GetLorgnetteManagerClient()->SetScanResponse("TestScanData");
chromeos::LorgnetteManagerClient::ScanProperties properties;
Scan(scanner.display_name, properties);
WaitForResult();
ASSERT_TRUE(scan_data());
EXPECT_EQ(scan_data().value(), "TestScanData");
}
} // namespace chromeos
// Copyright 2020 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 "chrome/browser/chromeos/scanning/lorgnette_scanner_manager_util.h"
#include "third_party/re2/src/re2/re2.h"
namespace chromeos {
namespace {
// Regular expressions used to determine whether a device name contains an IPv4
// address or URL.
constexpr char kIpv4Pattern[] = R"((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))";
constexpr char kUrlPattern[] = R"((://))";
} // namespace
void ParseScannerName(const std::string& scanner_name,
std::string& ip_address_out,
ScanProtocol& protocol_out) {
if (RE2::PartialMatch(scanner_name, kIpv4Pattern, &ip_address_out) ||
RE2::PartialMatch(scanner_name, kUrlPattern)) {
protocol_out = ScanProtocol::kLegacyNetwork;
return;
}
protocol_out = ScanProtocol::kLegacyUsb;
}
} // namespace chromeos
// Copyright 2020 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.
// Utility functions for the LorgnetteScannerManager.
#ifndef CHROME_BROWSER_CHROMEOS_SCANNING_LORGNETTE_SCANNER_MANAGER_UTIL_H_
#define CHROME_BROWSER_CHROMEOS_SCANNING_LORGNETTE_SCANNER_MANAGER_UTIL_H_
#include <string>
#include "chromeos/scanning/scanner.h"
namespace chromeos {
// Attempts to parse |scanner_name| to find an IP address and determine the scan
// protocol it corresponds to. If an IP address is found, it is returned in
// |ip_address_out|.
void ParseScannerName(const std::string& scanner_name,
std::string& ip_address_out,
ScanProtocol& protocol_out);
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_SCANNING_LORGNETTE_SCANNER_MANAGER_UTIL_H_
// Copyright 2020 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 "chrome/browser/chromeos/scanning/lorgnette_scanner_manager_util.h"
#include <string>
#include "chromeos/scanning/scanner.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
// Test that parsing a scanner name with an IP address successfully extracts the
// IP address and sets the protocol to kLegacyNetwork.
TEST(LorgnetteScannerManagerUtilTest, ParseNameWithIPAddress) {
const std::string scanner_name = "test:MX3100_192.168.0.3";
std::string ip_address;
ScanProtocol protocol;
ParseScannerName(scanner_name, ip_address, protocol);
EXPECT_EQ(ip_address, "192.168.0.3");
EXPECT_EQ(protocol, ScanProtocol::kLegacyNetwork);
}
// Test that parsing a scanner name with a URL successfully sets the protocol to
// kLegacyNetwork.
TEST(LorgnetteScannerManagerUtilTest, ParseNameWithUrl) {
const std::string scanner_name = "http://testscanner.domain.org";
std::string ip_address;
ScanProtocol protocol;
ParseScannerName(scanner_name, ip_address, protocol);
EXPECT_TRUE(ip_address.empty());
EXPECT_EQ(protocol, ScanProtocol::kLegacyNetwork);
}
// Test that parsing a scanner name without an IP address or URL successfully
// sets the protocol to kLegacyUsb.
TEST(LorgnetteScannerManagerUtilTest, ParseNameWithVidPid) {
const std::string scanner_name = "test:04A91752_94370B";
std::string ip_address;
ScanProtocol protocol;
ParseScannerName(scanner_name, ip_address, protocol);
EXPECT_TRUE(ip_address.empty());
EXPECT_EQ(protocol, ScanProtocol::kLegacyUsb);
}
} // namespace chromeos
...@@ -32,7 +32,7 @@ class CHROMEOS_EXPORT ScannerDetector { ...@@ -32,7 +32,7 @@ class CHROMEOS_EXPORT ScannerDetector {
// Registers the callback used to provide notifications when scanners are // Registers the callback used to provide notifications when scanners are
// detected. // detected.
using OnScannersDetectedCallback = using OnScannersDetectedCallback =
base::RepeatingCallback<void(const std::vector<Scanner>& scanners)>; base::RepeatingCallback<void(std::vector<Scanner> scanners)>;
virtual void RegisterScannersDetectedCallback( virtual void RegisterScannersDetectedCallback(
OnScannersDetectedCallback callback) = 0; OnScannersDetectedCallback callback) = 0;
......
...@@ -169,8 +169,8 @@ class ZeroconfScannerDetectorTest : public testing::Test { ...@@ -169,8 +169,8 @@ class ZeroconfScannerDetectorTest : public testing::Test {
} }
// ScannerDetector callback. // ScannerDetector callback.
void OnScannersDetected(const std::vector<Scanner>& scanners) { void OnScannersDetected(std::vector<Scanner> scanners) {
scanners_ = scanners; scanners_ = std::move(scanners);
} }
protected: protected:
......
...@@ -67,7 +67,8 @@ base::Optional<Scanner> CreateSaneAirscanScanner( ...@@ -67,7 +67,8 @@ base::Optional<Scanner> CreateSaneAirscanScanner(
const std::string& service_type, const std::string& service_type,
const std::string& rs, const std::string& rs,
const net::IPAddress& ip_address, const net::IPAddress& ip_address,
int port) { int port,
bool usable) {
std::string scheme; std::string scheme;
ScanProtocol protocol = ScanProtocol::kUnknown; ScanProtocol protocol = ScanProtocol::kUnknown;
SetSchemeAndProtocol(service_type, scheme, protocol); SetSchemeAndProtocol(service_type, scheme, protocol);
...@@ -78,7 +79,8 @@ base::Optional<Scanner> CreateSaneAirscanScanner( ...@@ -78,7 +79,8 @@ base::Optional<Scanner> CreateSaneAirscanScanner(
Scanner scanner; Scanner scanner;
scanner.display_name = name; scanner.display_name = name;
scanner.device_names[protocol].insert(ScannerDeviceName(device_name)); scanner.device_names[protocol].emplace(
ScannerDeviceName(device_name, usable));
scanner.ip_addresses.insert(ip_address); scanner.ip_addresses.insert(ip_address);
return scanner; return scanner;
} }
......
...@@ -26,7 +26,8 @@ base::Optional<Scanner> CreateSaneAirscanScanner( ...@@ -26,7 +26,8 @@ base::Optional<Scanner> CreateSaneAirscanScanner(
const std::string& service_type, const std::string& service_type,
const std::string& rs, const std::string& rs,
const net::IPAddress& ip_address, const net::IPAddress& ip_address,
int port); int port,
bool usable = true);
} // namespace chromeos } // namespace chromeos
......
...@@ -9,6 +9,10 @@ namespace chromeos { ...@@ -9,6 +9,10 @@ namespace chromeos {
ScannerDeviceName::ScannerDeviceName(const std::string& device_name) ScannerDeviceName::ScannerDeviceName(const std::string& device_name)
: device_name(device_name) {} : device_name(device_name) {}
ScannerDeviceName::ScannerDeviceName(const std::string& device_name,
bool usable)
: device_name(device_name), usable(usable) {}
ScannerDeviceName::~ScannerDeviceName() = default; ScannerDeviceName::~ScannerDeviceName() = default;
ScannerDeviceName::ScannerDeviceName(const ScannerDeviceName& other) = default; ScannerDeviceName::ScannerDeviceName(const ScannerDeviceName& other) = default;
......
...@@ -25,6 +25,7 @@ enum class CHROMEOS_EXPORT ScanProtocol { ...@@ -25,6 +25,7 @@ enum class CHROMEOS_EXPORT ScanProtocol {
struct CHROMEOS_EXPORT ScannerDeviceName { struct CHROMEOS_EXPORT ScannerDeviceName {
explicit ScannerDeviceName(const std::string& device_name); explicit ScannerDeviceName(const std::string& device_name);
ScannerDeviceName(const std::string& device_name, bool usable);
~ScannerDeviceName(); ~ScannerDeviceName();
ScannerDeviceName(const ScannerDeviceName& scanner_device_name); ScannerDeviceName(const ScannerDeviceName& scanner_device_name);
...@@ -56,7 +57,7 @@ struct CHROMEOS_EXPORT Scanner { ...@@ -56,7 +57,7 @@ struct CHROMEOS_EXPORT Scanner {
// Map of ScanProtocol to a set of corresponding ScannerDeviceNames that can // Map of ScanProtocol to a set of corresponding ScannerDeviceNames that can
// be used with the lorgnette D-Bus service. Clients are responsible for // be used with the lorgnette D-Bus service. Clients are responsible for
// selecting which device name to use. // selecting which device name to use.
base::flat_map<ScanProtocol, std::set<ScannerDeviceName>> device_names; base::flat_map<ScanProtocol, base::flat_set<ScannerDeviceName>> device_names;
// Known IP addresses for this scanner. Used to deduplicate network scanners // Known IP addresses for this scanner. Used to deduplicate network scanners
// from multiple sources. // from multiple sources.
......
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