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

scanning: Get capabilities with ScanService

Add GetScannerCapabilities() to the ScanService to allow clients to get
capabilities for a selected scanner.

Bug: 1059779
Change-Id: I40af92cef6f767a08be9851f1fe5c3cb200ac408
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2404662
Commit-Queue: Jesse Schettler <jschettler@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarZentaro Kavanagh <zentaro@chromium.org>
Cr-Commit-Position: refs/heads/master@{#809378}
parent bda1425f
......@@ -2470,6 +2470,8 @@ source_set("chromeos") {
"scanning/scan_service_factory.cc",
"scanning/scan_service_factory.h",
"scanning/scanner_detector.h",
"scanning/scanning_type_converters.cc",
"scanning/scanning_type_converters.h",
"scanning/zeroconf_scanner_detector.cc",
"scanning/zeroconf_scanner_detector.h",
"scanning/zeroconf_scanner_detector_utils.cc",
......@@ -3568,6 +3570,7 @@ source_set("unit_tests") {
"scanning/lorgnette_scanner_manager_unittest.cc",
"scanning/lorgnette_scanner_manager_util_unittest.cc",
"scanning/scan_service_unittest.cc",
"scanning/scanning_type_converters_unittest.cc",
"scanning/zeroconf_scanner_detector_unittest.cc",
"scheduler_configuration_manager_unittest.cc",
"session_length_limiter_unittest.cc",
......
file://chromeos/scanning/OWNERS
per-file *_type_converter*.*=set noparent
per-file *_type_converter*.*=file://ipc/SECURITY_OWNERS
......@@ -10,6 +10,7 @@
#include "base/check.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/chromeos/scanning/lorgnette_scanner_manager.h"
#include "chrome/browser/chromeos/scanning/scanning_type_converters.h"
namespace chromeos {
......@@ -32,6 +33,22 @@ void ScanService::GetScanners(GetScannersCallback callback) {
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ScanService::GetScannerCapabilities(
const base::UnguessableToken& scanner_id,
GetScannerCapabilitiesCallback callback) {
const auto it = scanner_names_.find(scanner_id);
if (it == scanner_names_.end()) {
LOG(ERROR) << "Failed to find scanner name using the given scanner id.";
std::move(callback).Run(mojo_ipc::ScannerCapabilities::New());
return;
}
lorgnette_scanner_manager_->GetScannerCapabilities(
it->second,
base::BindOnce(&ScanService::OnScannerCapabilitiesReceived,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ScanService::BindInterface(
mojo::PendingReceiver<mojo_ipc::ScanService> pending_receiver) {
receiver_.Bind(std::move(pending_receiver));
......@@ -46,14 +63,30 @@ void ScanService::Shutdown() {
void ScanService::OnScannerNamesReceived(
GetScannersCallback callback,
std::vector<std::string> scanner_names) {
scanner_names_.clear();
scanner_names_.reserve(scanner_names.size());
std::vector<mojo_ipc::ScannerPtr> scanners;
scanners.reserve(scanner_names.size());
for (const auto& name : scanner_names) {
base::UnguessableToken id = base::UnguessableToken::Create();
scanner_names_[id] = name;
scanners.push_back(mojo_ipc::Scanner::New(id, base::UTF8ToUTF16(name)));
}
std::move(callback).Run(std::move(scanners));
}
void ScanService::OnScannerCapabilitiesReceived(
GetScannerCapabilitiesCallback callback,
base::Optional<lorgnette::ScannerCapabilities> capabilities) {
if (!capabilities) {
LOG(ERROR) << "Failed to get scanner capabilities.";
std::move(callback).Run(mojo_ipc::ScannerCapabilities::New());
return;
}
std::move(callback).Run(
mojo::ConvertTo<mojo_ipc::ScannerCapabilitiesPtr>(capabilities.value()));
}
} // namespace chromeos
......@@ -8,9 +8,12 @@
#include <string>
#include <vector>
#include "base/containers/flat_map.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/unguessable_token.h"
#include "chromeos/components/scanning/mojom/scanning.mojom.h"
#include "chromeos/dbus/lorgnette/lorgnette_service.pb.h"
#include "components/keyed_service/core/keyed_service.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
......@@ -32,6 +35,8 @@ class ScanService : public scanning::mojom::ScanService, public KeyedService {
// scanning::mojom::ScanService:
void GetScanners(GetScannersCallback callback) override;
void GetScannerCapabilities(const base::UnguessableToken& scanner_id,
GetScannerCapabilitiesCallback callback) override;
// Binds receiver_ by consuming |pending_receiver|.
void BindInterface(
......@@ -45,6 +50,16 @@ class ScanService : public scanning::mojom::ScanService, public KeyedService {
void OnScannerNamesReceived(GetScannersCallback callback,
std::vector<std::string> scanner_names);
// Processes the result of calling
// LorgnetteScannerManager::GetScannerCapabilities().
void OnScannerCapabilitiesReceived(
GetScannerCapabilitiesCallback callback,
base::Optional<lorgnette::ScannerCapabilities> capabilities);
// Map of scanner IDs to display names. Used to pass the correct display name
// to LorgnetteScannerManager when clients provide an ID.
base::flat_map<base::UnguessableToken, std::string> scanner_names_;
// Receives and dispatches method calls to this implementation of the
// chromeos::scanning::mojom::ScanService interface.
mojo::Receiver<scanning::mojom::ScanService> receiver_{this};
......
......@@ -6,11 +6,14 @@
#include <vector>
#include "base/optional.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/task_environment.h"
#include "base/unguessable_token.h"
#include "chrome/browser/chromeos/scanning/fake_lorgnette_scanner_manager.h"
#include "chromeos/components/scanning/mojom/scanning.mojom-test-utils.h"
#include "chromeos/components/scanning/mojom/scanning.mojom.h"
#include "chromeos/dbus/lorgnette/lorgnette_service.pb.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -24,6 +27,31 @@ namespace mojo_ipc = scanning::mojom;
constexpr char kFirstTestScannerName[] = "Test Scanner 1";
constexpr char kSecondTestScannerName[] = "Test Scanner 2";
// Document source name used for tests.
constexpr char kDocumentSourceName[] = "Flatbed";
// Resolutions used for tests.
constexpr uint32_t kFirstResolution = 75;
constexpr uint32_t kSecondResolution = 300;
// Returns a DocumentSource object.
lorgnette::DocumentSource CreateLorgnetteDocumentSource() {
lorgnette::DocumentSource source;
source.set_type(lorgnette::SOURCE_PLATEN);
source.set_name(kDocumentSourceName);
return source;
}
// Returns a ScannerCapabilities object.
lorgnette::ScannerCapabilities CreateLorgnetteScannerCapabilities() {
lorgnette::ScannerCapabilities caps;
*caps.add_sources() = CreateLorgnetteDocumentSource();
caps.add_color_modes(lorgnette::MODE_COLOR);
caps.add_resolutions(kFirstResolution);
caps.add_resolutions(kSecondResolution);
return caps;
}
} // namespace
class ScanServiceTest : public testing::Test {
......@@ -43,6 +71,17 @@ class ScanServiceTest : public testing::Test {
return scanners;
}
// Gets scanner capabilities for the scanner identified by |scanner_id| by
// calling ScanService::GetScannerCapabilities() via the mojo::Remote.
mojo_ipc::ScannerCapabilitiesPtr GetScannerCapabilities(
const base::UnguessableToken& scanner_id) {
mojo_ipc::ScannerCapabilitiesPtr caps =
mojo_ipc::ScannerCapabilities::New();
mojo_ipc::ScanServiceAsyncWaiter(scan_service_remote_.get())
.GetScannerCapabilities(scanner_id, &caps);
return caps;
}
protected:
FakeLorgnetteScannerManager fake_lorgnette_scanner_manager_;
......@@ -82,4 +121,47 @@ TEST_F(ScanServiceTest, UniqueScannerIds) {
EXPECT_NE(scanners[0]->id, scanners[1]->id);
}
// Test that attempting to get capabilities with a scanner ID that doesn't
// correspond to a scanner results in obtaining no capabilities.
TEST_F(ScanServiceTest, BadScannerId) {
auto caps = GetScannerCapabilities(base::UnguessableToken::Create());
EXPECT_TRUE(caps->sources.empty());
EXPECT_TRUE(caps->color_modes.empty());
EXPECT_TRUE(caps->resolutions.empty());
}
// Test that failing to obtain capabilities from the LorgnetteScannerManager
// results in obtaining no capabilities.
TEST_F(ScanServiceTest, NoCapabilities) {
fake_lorgnette_scanner_manager_.SetGetScannerNamesResponse(
{kFirstTestScannerName});
fake_lorgnette_scanner_manager_.SetGetScannerCapabilitiesResponse(
base::nullopt);
auto scanners = GetScanners();
ASSERT_EQ(scanners.size(), 1u);
auto caps = GetScannerCapabilities(scanners[0]->id);
EXPECT_TRUE(caps->sources.empty());
EXPECT_TRUE(caps->color_modes.empty());
EXPECT_TRUE(caps->resolutions.empty());
}
// Test that scanner capabilities can be obtained successfully.
TEST_F(ScanServiceTest, GetScannerCapabilities) {
fake_lorgnette_scanner_manager_.SetGetScannerNamesResponse(
{kFirstTestScannerName});
fake_lorgnette_scanner_manager_.SetGetScannerCapabilitiesResponse(
CreateLorgnetteScannerCapabilities());
auto scanners = GetScanners();
ASSERT_EQ(scanners.size(), 1u);
auto caps = GetScannerCapabilities(scanners[0]->id);
ASSERT_EQ(caps->sources.size(), 1u);
EXPECT_EQ(caps->sources[0]->type, mojo_ipc::SourceType::kFlatbed);
EXPECT_EQ(caps->sources[0]->name, kDocumentSourceName);
ASSERT_EQ(caps->color_modes.size(), 1u);
EXPECT_EQ(caps->color_modes[0], mojo_ipc::ColorMode::kColor);
ASSERT_EQ(caps->resolutions.size(), 2u);
EXPECT_EQ(caps->resolutions[0], kFirstResolution);
EXPECT_EQ(caps->resolutions[1], kSecondResolution);
}
} // 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/scanning_type_converters.h"
#include "base/notreached.h"
#include "chromeos/dbus/lorgnette/lorgnette_service.pb.h"
namespace mojo {
namespace {
namespace mojo_ipc = chromeos::scanning::mojom;
} // namespace
template <>
struct TypeConverter<mojo_ipc::ColorMode, lorgnette::ColorMode> {
static mojo_ipc::ColorMode Convert(lorgnette::ColorMode mode) {
switch (mode) {
case lorgnette::MODE_LINEART:
return mojo_ipc::ColorMode::kBlackAndWhite;
case lorgnette::MODE_GRAYSCALE:
return mojo_ipc::ColorMode::kGrayscale;
case lorgnette::MODE_COLOR:
return mojo_ipc::ColorMode::kColor;
case lorgnette::MODE_UNSPECIFIED:
case lorgnette::ColorMode_INT_MIN_SENTINEL_DO_NOT_USE_:
case lorgnette::ColorMode_INT_MAX_SENTINEL_DO_NOT_USE_:
NOTREACHED();
return mojo_ipc::ColorMode::kColor;
}
}
};
template <>
struct TypeConverter<mojo_ipc::SourceType, lorgnette::SourceType> {
static mojo_ipc::SourceType Convert(lorgnette::SourceType type) {
switch (type) {
case lorgnette::SOURCE_PLATEN:
return mojo_ipc::SourceType::kFlatbed;
case lorgnette::SOURCE_ADF_SIMPLEX:
return mojo_ipc::SourceType::kAdfSimplex;
case lorgnette::SOURCE_ADF_DUPLEX:
return mojo_ipc::SourceType::kAdfDuplex;
case lorgnette::SOURCE_UNSPECIFIED:
case lorgnette::SourceType_INT_MIN_SENTINEL_DO_NOT_USE_:
case lorgnette::SourceType_INT_MAX_SENTINEL_DO_NOT_USE_:
NOTREACHED();
return mojo_ipc::SourceType::kFlatbed;
}
}
};
// static
mojo_ipc::ScannerCapabilitiesPtr TypeConverter<mojo_ipc::ScannerCapabilitiesPtr,
lorgnette::ScannerCapabilities>::
Convert(const lorgnette::ScannerCapabilities& lorgnette_caps) {
mojo_ipc::ScannerCapabilities mojo_caps;
mojo_caps.sources.reserve(lorgnette_caps.sources().size());
for (const auto& source : lorgnette_caps.sources()) {
mojo_caps.sources.push_back(mojo_ipc::ScanSource::New(
mojo::ConvertTo<mojo_ipc::SourceType>(source.type()), source.name()));
}
mojo_caps.color_modes.reserve(lorgnette_caps.color_modes().size());
for (const auto& mode : lorgnette_caps.color_modes()) {
mojo_caps.color_modes.push_back(mojo::ConvertTo<mojo_ipc::ColorMode>(
static_cast<lorgnette::ColorMode>(mode)));
}
mojo_caps.resolutions.reserve(lorgnette_caps.resolutions().size());
for (const auto& res : lorgnette_caps.resolutions())
mojo_caps.resolutions.push_back(res);
return mojo_caps.Clone();
}
} // namespace mojo
// 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_SCANNING_TYPE_CONVERTERS_H_
#define CHROME_BROWSER_CHROMEOS_SCANNING_SCANNING_TYPE_CONVERTERS_H_
#include "chromeos/components/scanning/mojom/scanning.mojom.h"
#include "mojo/public/cpp/bindings/type_converter.h"
namespace lorgnette {
class ScannerCapabilities;
} // namespace lorgnette
namespace mojo {
template <>
struct TypeConverter<chromeos::scanning::mojom::ScannerCapabilitiesPtr,
lorgnette::ScannerCapabilities> {
static chromeos::scanning::mojom::ScannerCapabilitiesPtr Convert(
const lorgnette::ScannerCapabilities& lorgnette_caps);
};
} // namespace mojo
#endif // CHROME_BROWSER_CHROMEOS_SCANNING_SCANNING_TYPE_CONVERTERS_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/scanning_type_converters.h"
#include "chromeos/components/scanning/mojom/scanning.mojom.h"
#include "chromeos/dbus/lorgnette/lorgnette_service.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace {
namespace mojo_ipc = scanning::mojom;
// POD struct for ScanningTypeConvertersTest.
struct ScanningTypeConvertersTestParams {
lorgnette::SourceType lorgnette_source_type;
lorgnette::ColorMode lorgnette_color_mode;
mojo_ipc::SourceType mojom_source_type;
mojo_ipc::ColorMode mojom_color_mode;
};
// Document source name used for tests.
constexpr char kDocumentSourceName[] = "Test Name";
// Resolutions used for tests.
constexpr uint32_t kFirstResolution = 75;
constexpr uint32_t kSecondResolution = 300;
// Returns a DocumentSource object with the given |source_type|.
lorgnette::DocumentSource CreateLorgnetteDocumentSource(
lorgnette::SourceType source_type) {
lorgnette::DocumentSource source;
source.set_type(source_type);
source.set_name(kDocumentSourceName);
return source;
}
// Returns a ScannerCapabilities object with the given |source_type| and
// |color_mode|.
lorgnette::ScannerCapabilities CreateLorgnetteScannerCapabilities(
lorgnette::SourceType source_type,
lorgnette::ColorMode color_mode) {
lorgnette::ScannerCapabilities caps;
*caps.add_sources() = CreateLorgnetteDocumentSource(source_type);
caps.add_color_modes(color_mode);
caps.add_resolutions(kFirstResolution);
caps.add_resolutions(kSecondResolution);
return caps;
}
} // namespace
// Tests that each possible lorgnette::ScannerCapabilities object can be
// correctly converted into a mojo_ipc::ScannerCapabilitiesPtr.
//
// This is a parameterized test with the following parameters (accessed through
// ScanningTypeConvertersTestParams):
// * |lorgnette_source_type| - the lorgnette::SourceType to convert.
// * |lorgnette_color_mode| - the lorgnette::ColorMode to convert.
// * |mojom_source_type| - the expected mojo_ipc::SourceType.
// * |mojom_color_mode| - the expected mojo_ipc::ColorMode.
class ScanningTypeConvertersTest
: public testing::Test,
public testing::WithParamInterface<ScanningTypeConvertersTestParams> {
protected:
// Accessors to the test parameters returned by gtest's GetParam():
ScanningTypeConvertersTestParams params() const { return GetParam(); }
};
// Test that lorgnette::ScannerCapabilities can be converted into a
// mojo_ipc::ScannerCapabilitiesPtr.
TEST_P(ScanningTypeConvertersTest, LorgnetteCapsToMojom) {
mojo_ipc::ScannerCapabilitiesPtr mojo_caps =
mojo::ConvertTo<mojo_ipc::ScannerCapabilitiesPtr>(
CreateLorgnetteScannerCapabilities(params().lorgnette_source_type,
params().lorgnette_color_mode));
ASSERT_EQ(mojo_caps->sources.size(), 1u);
EXPECT_EQ(mojo_caps->sources[0]->type, params().mojom_source_type);
EXPECT_EQ(mojo_caps->sources[0]->name, kDocumentSourceName);
ASSERT_EQ(mojo_caps->color_modes.size(), 1u);
EXPECT_EQ(mojo_caps->color_modes[0], params().mojom_color_mode);
ASSERT_EQ(mojo_caps->resolutions.size(), 2u);
EXPECT_EQ(mojo_caps->resolutions[0], kFirstResolution);
EXPECT_EQ(mojo_caps->resolutions[1], kSecondResolution);
}
INSTANTIATE_TEST_SUITE_P(
,
ScanningTypeConvertersTest,
testing::Values(
ScanningTypeConvertersTestParams{lorgnette::SOURCE_PLATEN,
lorgnette::MODE_LINEART,
mojo_ipc::SourceType::kFlatbed,
mojo_ipc::ColorMode::kBlackAndWhite},
ScanningTypeConvertersTestParams{
lorgnette::SOURCE_ADF_SIMPLEX, lorgnette::MODE_GRAYSCALE,
mojo_ipc::SourceType::kAdfSimplex, mojo_ipc::ColorMode::kGrayscale},
ScanningTypeConvertersTestParams{
lorgnette::SOURCE_ADF_DUPLEX, lorgnette::MODE_COLOR,
mojo_ipc::SourceType::kAdfDuplex, mojo_ipc::ColorMode::kColor}));
} // namespace chromeos
......@@ -7,6 +7,39 @@ module chromeos.scanning.mojom;
import "mojo/public/mojom/base/string16.mojom";
import "mojo/public/mojom/base/unguessable_token.mojom";
// The color modes that can be used to perform a scan.
enum ColorMode {
kBlackAndWhite,
kGrayscale,
kColor,
};
// The source types from which a scan can be obtained.
enum SourceType {
// A flatbed that scans a single page.
kFlatbed,
// An automatic document feeder that scans a single side of each page.
kAdfSimplex,
// An automatic document feeder that scans both sides of each page.
kAdfDuplex,
};
// The source from which a scan can be obtained.
struct ScanSource {
// The type of this source.
SourceType type;
// The name of this source. Source names are unique to each SANE backend and
// are required to perform scans.
string name;
};
// The capabilities a scanner supports.
struct ScannerCapabilities {
array<ScanSource> sources;
array<ColorMode> color_modes;
array<uint32> resolutions;
};
// Represents a connected scanner.
struct Scanner {
// The scanner's unique identifier.
......@@ -23,4 +56,10 @@ interface ScanService {
// implemented in a separate method to minimize the amount of time clients
// must wait before receiving the scanners and displaying their display names.
GetScanners() => (array<Scanner> scanners);
// Returns the capabilities of the scanner identified by |scanner_id|.
// TODO(jschettler): Return a boolean to indicate whether the capabilities
// were obtained via a secure protocol.
GetScannerCapabilities(mojo_base.mojom.UnguessableToken scanner_id)
=> (ScannerCapabilities capabilities);
};
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