Commit 286a19d6 authored by Fletcher Woodruff's avatar Fletcher Woodruff Committed by Commit Bot

Add StartScan API in lorgnette_manager_client

* Add a function StartScan to the lorgnette_manager_client API that
  allows scanning images using lorgnette's StartScan function.
  - Clients of the API can pass a RepeatingCallback<void(int)>, which
    will be invoked whenever the scan progress has updated.

* Implement StartScan for fake_lorgnette_manager_client.
* Update document_scan_api and unittests to use StartScan instead of
  ScanImageToString.

Bug: b:158504381
Change-Id: I6ca9e7645dc47a72d517d3d7480ea0f12e840e0f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2332852Reviewed-by: default avatarSteven Bennetts <stevenjb@chromium.org>
Reviewed-by: default avatarJesse Schettler <jschettler@chromium.org>
Commit-Queue: Fletcher Woodruff <fletcherw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#795215}
parent b2c1e00e
...@@ -86,9 +86,10 @@ void DocumentScanScanFunction::OnScannerListReceived( ...@@ -86,9 +86,10 @@ void DocumentScanScanFunction::OnScannerListReceived(
const auto& scanner = response->scanners()[0]; const auto& scanner = response->scanners()[0];
chromeos::LorgnetteManagerClient::ScanProperties properties; chromeos::LorgnetteManagerClient::ScanProperties properties;
properties.mode = lorgnette::kScanPropertyModeColor; properties.mode = lorgnette::kScanPropertyModeColor;
GetLorgnetteManagerClient()->ScanImageToString( GetLorgnetteManagerClient()->StartScan(
scanner.name(), properties, scanner.name(), properties,
base::BindOnce(&DocumentScanScanFunction::OnResultsReceived, this)); base::BindOnce(&DocumentScanScanFunction::OnResultsReceived, this),
base::nullopt);
} }
void DocumentScanScanFunction::OnResultsReceived( void DocumentScanScanFunction::OnResultsReceived(
......
...@@ -100,7 +100,7 @@ TEST_F(DocumentScanScanFunctionTest, UnsupportedMimeTypesError) { ...@@ -100,7 +100,7 @@ TEST_F(DocumentScanScanFunctionTest, UnsupportedMimeTypesError) {
TEST_F(DocumentScanScanFunctionTest, ScanImageError) { TEST_F(DocumentScanScanFunctionTest, ScanImageError) {
GetLorgnetteManagerClient()->SetListScannersResponse( GetLorgnetteManagerClient()->SetListScannersResponse(
CreateListScannersResponse()); CreateListScannersResponse());
GetLorgnetteManagerClient()->SetScanImageToStringResponse(base::nullopt); GetLorgnetteManagerClient()->SetScanResponse(base::nullopt);
EXPECT_EQ("Failed to scan image", EXPECT_EQ("Failed to scan image",
RunFunctionAndReturnError("[{\"mimeTypes\": [\"image/png\"]}]")); RunFunctionAndReturnError("[{\"mimeTypes\": [\"image/png\"]}]"));
} }
...@@ -108,7 +108,7 @@ TEST_F(DocumentScanScanFunctionTest, ScanImageError) { ...@@ -108,7 +108,7 @@ TEST_F(DocumentScanScanFunctionTest, ScanImageError) {
TEST_F(DocumentScanScanFunctionTest, Success) { TEST_F(DocumentScanScanFunctionTest, Success) {
GetLorgnetteManagerClient()->SetListScannersResponse( GetLorgnetteManagerClient()->SetListScannersResponse(
CreateListScannersResponse()); CreateListScannersResponse());
GetLorgnetteManagerClient()->SetScanImageToStringResponse("PrettyPicture"); GetLorgnetteManagerClient()->SetScanResponse("PrettyPicture");
std::unique_ptr<base::DictionaryValue> result(RunFunctionAndReturnDictionary( std::unique_ptr<base::DictionaryValue> result(RunFunctionAndReturnDictionary(
function_.get(), "[{\"mimeTypes\": [\"image/png\"]}]")); function_.get(), "[{\"mimeTypes\": [\"image/png\"]}]"));
ASSERT_NE(nullptr, result.get()); ASSERT_NE(nullptr, result.get());
......
...@@ -35,13 +35,31 @@ void FakeLorgnetteManagerClient::ScanImageToString( ...@@ -35,13 +35,31 @@ void FakeLorgnetteManagerClient::ScanImageToString(
base::BindOnce(std::move(callback), std::move(scan_image_response_))); base::BindOnce(std::move(callback), std::move(scan_image_response_)));
} }
void FakeLorgnetteManagerClient::StartScan(
std::string device_name,
const ScanProperties& properties,
DBusMethodCallback<std::string> completion_callback,
base::Optional<base::RepeatingCallback<void(int)>> progress_callback) {
// Simulate progress reporting for the scan job.
if (progress_callback.has_value()) {
base::RepeatingCallback<void(int)> callback = progress_callback.value();
for (int progress : {7, 22, 40, 42, 59, 74, 95}) {
callback.Run(progress);
}
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(completion_callback),
std::move(scan_image_response_)));
}
void FakeLorgnetteManagerClient::SetListScannersResponse( void FakeLorgnetteManagerClient::SetListScannersResponse(
const base::Optional<lorgnette::ListScannersResponse>& const base::Optional<lorgnette::ListScannersResponse>&
list_scanners_response) { list_scanners_response) {
list_scanners_response_ = list_scanners_response; list_scanners_response_ = list_scanners_response;
} }
void FakeLorgnetteManagerClient::SetScanImageToStringResponse( void FakeLorgnetteManagerClient::SetScanResponse(
const base::Optional<std::string>& scan_image_response) { const base::Optional<std::string>& scan_image_response) {
scan_image_response_ = scan_image_response; scan_image_response_ = scan_image_response;
} }
......
...@@ -30,14 +30,19 @@ class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeLorgnetteManagerClient ...@@ -30,14 +30,19 @@ class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeLorgnetteManagerClient
const ScanProperties& properties, const ScanProperties& properties,
DBusMethodCallback<std::string> callback) override; DBusMethodCallback<std::string> callback) override;
void StartScan(std::string device_name,
const ScanProperties& properties,
DBusMethodCallback<std::string> completion_callback,
base::Optional<base::RepeatingCallback<void(int)>>
progress_callback) override;
// Sets the response returned by ListScanners(). // Sets the response returned by ListScanners().
void SetListScannersResponse( void SetListScannersResponse(
const base::Optional<lorgnette::ListScannersResponse>& const base::Optional<lorgnette::ListScannersResponse>&
list_scanners_response); list_scanners_response);
// Sets the response returned by ScanImageToString(). // Sets the response returned by ScanImageToString() and StartScan().
void SetScanImageToStringResponse( void SetScanResponse(const base::Optional<std::string>& scan_image_response);
const base::Optional<std::string>& scan_image_response);
private: private:
base::Optional<lorgnette::ListScannersResponse> list_scanners_response_; base::Optional<lorgnette::ListScannersResponse> list_scanners_response_;
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/files/scoped_file.h" #include "base/files/scoped_file.h"
#include "base/location.h" #include "base/location.h"
#include "base/logging.h" #include "base/logging.h"
...@@ -18,7 +19,9 @@ ...@@ -18,7 +19,9 @@
#include "base/optional.h" #include "base/optional.h"
#include "base/task/post_task.h" #include "base/task/post_task.h"
#include "base/task/thread_pool.h" #include "base/task/thread_pool.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "chromeos/dbus/lorgnette/lorgnette_service.pb.h"
#include "chromeos/dbus/pipe_reader.h" #include "chromeos/dbus/pipe_reader.h"
#include "dbus/bus.h" #include "dbus/bus.h"
#include "dbus/message.h" #include "dbus/message.h"
...@@ -94,11 +97,65 @@ class LorgnetteManagerClientImpl : public LorgnetteManagerClient { ...@@ -94,11 +97,65 @@ class LorgnetteManagerClientImpl : public LorgnetteManagerClient {
std::move(scan_data_reader))); std::move(scan_data_reader)));
} }
void StartScan(std::string device_name,
const ScanProperties& properties,
DBusMethodCallback<std::string> completion_callback,
base::Optional<base::RepeatingCallback<void(int)>>
progress_callback) override {
lorgnette::StartScanRequest request;
request.set_device_name(device_name);
request.mutable_settings()->set_resolution(properties.resolution_dpi);
lorgnette::ColorMode mode = lorgnette::MODE_UNSPECIFIED;
// Defined in system_api/dbus/lorgnette/dbus-constants.h
if (properties.mode == lorgnette::kScanPropertyModeColor) {
mode = lorgnette::MODE_COLOR;
} else if (properties.mode == lorgnette::kScanPropertyModeGray) {
mode = lorgnette::MODE_GRAYSCALE;
} else if (properties.mode == lorgnette::kScanPropertyModeLineart) {
mode = lorgnette::MODE_LINEART;
}
request.mutable_settings()->set_color_mode(mode);
dbus::MethodCall method_call(lorgnette::kManagerServiceInterface,
lorgnette::kStartScanMethod);
dbus::MessageWriter writer(&method_call);
if (!writer.AppendProtoAsArrayOfBytes(request)) {
LOG(ERROR) << "Failed to encode StartScanRequest protobuf";
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(completion_callback), base::nullopt));
return;
}
auto scan_data_reader = std::make_unique<ScanDataReader>();
base::ScopedFD fd = scan_data_reader->Start();
writer.AppendFileDescriptor(fd.get());
ScanJobState state;
state.completion_callback = std::move(completion_callback);
state.progress_callback = progress_callback;
state.scan_data_reader = std::move(scan_data_reader);
lorgnette_daemon_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&LorgnetteManagerClientImpl::OnStartScanResponse,
weak_ptr_factory_.GetWeakPtr(), std::move(state)));
}
protected: protected:
void Init(dbus::Bus* bus) override { void Init(dbus::Bus* bus) override {
lorgnette_daemon_proxy_ = lorgnette_daemon_proxy_ =
bus->GetObjectProxy(lorgnette::kManagerServiceName, bus->GetObjectProxy(lorgnette::kManagerServiceName,
dbus::ObjectPath(lorgnette::kManagerServicePath)); dbus::ObjectPath(lorgnette::kManagerServicePath));
lorgnette_daemon_proxy_->ConnectToSignal(
lorgnette::kManagerServiceInterface,
lorgnette::kScanStatusChangedSignal,
base::BindRepeating(
&LorgnetteManagerClientImpl::ScanStatusChangedReceived,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&LorgnetteManagerClientImpl::ScanStatusChangedConnected,
weak_ptr_factory_.GetWeakPtr()));
} }
private: private:
...@@ -167,6 +224,16 @@ class LorgnetteManagerClientImpl : public LorgnetteManagerClient { ...@@ -167,6 +224,16 @@ class LorgnetteManagerClientImpl : public LorgnetteManagerClient {
DISALLOW_COPY_AND_ASSIGN(ScanDataReader); DISALLOW_COPY_AND_ASSIGN(ScanDataReader);
}; };
// The state tracked for an in-progress scan job.
// Contains callbacks used to report progress and job completion or failure,
// as well as a ScanDataReader which is responsible for reading from the pipe
// of data into a string.
struct ScanJobState {
DBusMethodCallback<std::string> completion_callback;
base::Optional<base::RepeatingCallback<void(int)>> progress_callback;
std::unique_ptr<ScanDataReader> scan_data_reader;
};
// Called when ListScanners completes. // Called when ListScanners completes.
void OnListScanners( void OnListScanners(
DBusMethodCallback<lorgnette::ListScannersResponse> callback, DBusMethodCallback<lorgnette::ListScannersResponse> callback,
...@@ -214,7 +281,77 @@ class LorgnetteManagerClientImpl : public LorgnetteManagerClient { ...@@ -214,7 +281,77 @@ class LorgnetteManagerClientImpl : public LorgnetteManagerClient {
std::move(callback).Run(std::move(data)); std::move(callback).Run(std::move(data));
} }
void OnStartScanResponse(ScanJobState state, dbus::Response* response) {
if (!response) {
LOG(ERROR) << "Failed to obtain StartScanResponse";
std::move(state.completion_callback).Run(base::nullopt);
return;
}
lorgnette::StartScanResponse response_proto;
dbus::MessageReader reader(response);
if (!reader.PopArrayOfBytesAsProto(&response_proto)) {
LOG(ERROR) << "Failed to decode StartScanResponse proto";
std::move(state.completion_callback).Run(base::nullopt);
return;
}
if (response_proto.state() == lorgnette::SCAN_STATE_FAILED) {
LOG(ERROR) << "Starting Scan failed: " << response_proto.failure_reason();
std::move(state.completion_callback).Run(base::nullopt);
return;
}
scan_job_state_[response_proto.scan_uuid()] = std::move(state);
}
void ScanStatusChangedReceived(dbus::Signal* signal) {
dbus::MessageReader reader(signal);
lorgnette::ScanStatusChangedSignal signal_proto;
if (!reader.PopArrayOfBytesAsProto(&signal_proto)) {
LOG(ERROR) << "Failed to decode ScanStatusChangedSignal proto";
return;
}
if (!base::Contains(scan_job_state_, signal_proto.scan_uuid())) {
LOG(ERROR) << "Received signal for unrecognized scan job: "
<< signal_proto.scan_uuid();
return;
}
ScanJobState& state = scan_job_state_[signal_proto.scan_uuid()];
if (signal_proto.state() == lorgnette::SCAN_STATE_FAILED) {
LOG(ERROR) << "Scan job " << signal_proto.scan_uuid()
<< " failed: " << signal_proto.failure_reason();
std::move(state.completion_callback).Run(base::nullopt);
scan_job_state_.erase(signal_proto.scan_uuid());
} else if (signal_proto.state() == lorgnette::SCAN_STATE_COMPLETED) {
VLOG(1) << "Scan job " << signal_proto.scan_uuid()
<< " completed successfully";
ScanDataReader* reader = state.scan_data_reader.get();
reader->Wait(base::BindOnce(
&LorgnetteManagerClientImpl::OnScanDataCompleted,
weak_ptr_factory_.GetWeakPtr(), std::move(state.completion_callback),
std::move(state.scan_data_reader)));
scan_job_state_.erase(signal_proto.scan_uuid());
} else if (signal_proto.state() == lorgnette::SCAN_STATE_IN_PROGRESS &&
state.progress_callback.has_value()) {
state.progress_callback.value().Run(signal_proto.progress());
}
}
void ScanStatusChangedConnected(const std::string& interface_name,
const std::string& signal_name,
bool success) {
LOG_IF(WARNING, !success)
<< "Failed to connect to ScanStatusChanged signal.";
}
dbus::ObjectProxy* lorgnette_daemon_proxy_ = nullptr; dbus::ObjectProxy* lorgnette_daemon_proxy_ = nullptr;
// Map from scan UUIDs to ScanDataReader and callbacks for reporting scan
// progress and completion.
base::flat_map<std::string, ScanJobState> scan_job_state_;
base::WeakPtrFactory<LorgnetteManagerClientImpl> weak_ptr_factory_{this}; base::WeakPtrFactory<LorgnetteManagerClientImpl> weak_ptr_factory_{this};
}; };
......
...@@ -43,6 +43,18 @@ class COMPONENT_EXPORT(CHROMEOS_DBUS) LorgnetteManagerClient ...@@ -43,6 +43,18 @@ class COMPONENT_EXPORT(CHROMEOS_DBUS) LorgnetteManagerClient
const ScanProperties& properties, const ScanProperties& properties,
DBusMethodCallback<std::string> callback) = 0; DBusMethodCallback<std::string> 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.
//
// If |progress_callback| is provided, it will be called as scan progress
// increases.The progress will be passed as a value from 0-100.
virtual void StartScan(
std::string device_name,
const ScanProperties& properties,
DBusMethodCallback<std::string> completion_callback,
base::Optional<base::RepeatingCallback<void(int)>> progress_callback) = 0;
// Factory function, creates a new instance and returns ownership. // Factory function, creates a new instance and returns ownership.
// For normal usage, access the singleton via DBusThreadManager::Get(). // For normal usage, access the singleton via DBusThreadManager::Get().
static std::unique_ptr<LorgnetteManagerClient> Create(); static std::unique_ptr<LorgnetteManagerClient> Create();
......
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