1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chromeos/dbus/lorgnette_manager_client.h"
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/callback.h"
12#include "base/location.h"
13#include "base/memory/ref_counted_memory.h"
14#include "base/threading/worker_pool.h"
15#include "dbus/bus.h"
16#include "dbus/message.h"
17#include "dbus/object_path.h"
18#include "dbus/object_proxy.h"
19#include "net/base/file_stream.h"
20#include "third_party/cros_system_api/dbus/service_constants.h"
21
22namespace chromeos {
23
24// The LorgnetteManagerClient implementation used in production.
25class LorgnetteManagerClientImpl : public LorgnetteManagerClient {
26 public:
27  LorgnetteManagerClientImpl() :
28      lorgnette_daemon_proxy_(NULL), weak_ptr_factory_(this) {}
29
30  virtual ~LorgnetteManagerClientImpl() {}
31
32  virtual void ListScanners(const ListScannersCallback& callback) OVERRIDE {
33    dbus::MethodCall method_call(lorgnette::kManagerServiceInterface,
34                                 lorgnette::kListScannersMethod);
35    lorgnette_daemon_proxy_->CallMethod(
36        &method_call,
37        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
38        base::Bind(&LorgnetteManagerClientImpl::OnListScanners,
39                   weak_ptr_factory_.GetWeakPtr(),
40                   callback));
41  }
42
43  // LorgnetteManagerClient override.
44  virtual void ScanImage(std::string device_name,
45                         base::PlatformFile file,
46                         const ScanProperties& properties,
47                         const ScanImageCallback& callback) OVERRIDE {
48    dbus::FileDescriptor* file_descriptor = new dbus::FileDescriptor();
49    file_descriptor->PutValue(file);
50    // Punt descriptor validity check to a worker thread; on return we'll
51    // issue the D-Bus request to stop tracing and collect results.
52    base::WorkerPool::PostTaskAndReply(
53        FROM_HERE,
54        base::Bind(&LorgnetteManagerClientImpl::CheckValidity,
55                   file_descriptor),
56        base::Bind(&LorgnetteManagerClientImpl::OnCheckValidityScanImage,
57                   weak_ptr_factory_.GetWeakPtr(),
58                   base::Owned(file_descriptor),
59                   device_name,
60                   properties,
61                   callback),
62        false);
63  }
64
65 protected:
66  virtual void Init(dbus::Bus* bus) OVERRIDE {
67    lorgnette_daemon_proxy_ =
68        bus->GetObjectProxy(lorgnette::kManagerServiceName,
69                            dbus::ObjectPath(lorgnette::kManagerServicePath));
70  }
71
72 private:
73  // Called when ListScanners completes.
74  void OnListScanners(const ListScannersCallback& callback,
75                      dbus::Response* response) {
76    ScannerTable scanners;
77    dbus::MessageReader table_reader(NULL);
78    if (!response || !dbus::MessageReader(response).PopArray(&table_reader)) {
79      callback.Run(false, scanners);
80      return;
81    }
82
83    bool decode_failure = false;
84    while (table_reader.HasMoreData()) {
85      std::string device_name;
86      dbus::MessageReader device_entry_reader(NULL);
87      dbus::MessageReader device_element_reader(NULL);
88      if (!table_reader.PopDictEntry(&device_entry_reader) ||
89          !device_entry_reader.PopString(&device_name) ||
90          !device_entry_reader.PopArray(&device_element_reader)) {
91        decode_failure = true;
92        break;
93      }
94
95      ScannerTableEntry scanner_entry;
96      while (device_element_reader.HasMoreData()) {
97        dbus::MessageReader device_attribute_reader(NULL);
98        std::string attribute;
99        std::string value;
100        if (!device_element_reader.PopDictEntry(&device_attribute_reader) ||
101            !device_attribute_reader.PopString(&attribute) ||
102            !device_attribute_reader.PopString(&value)) {
103          decode_failure = true;
104          break;
105        }
106        scanner_entry[attribute] = value;
107      }
108
109      if (decode_failure)
110          break;
111
112      scanners[device_name] = scanner_entry;
113    }
114
115    if (decode_failure) {
116      LOG(ERROR) << "Failed to decode response from ListScanners";
117      callback.Run(false, scanners);
118    } else {
119      callback.Run(true, scanners);
120    }
121  }
122
123  // Called to check descriptor validity on a thread where i/o is permitted.
124  static void CheckValidity(dbus::FileDescriptor* file_descriptor) {
125    file_descriptor->CheckValidity();
126  }
127
128  // Called when a CheckValidity response is received.
129  void OnCheckValidityScanImage(
130      dbus::FileDescriptor* file_descriptor,
131      std::string device_name,
132      const ScanProperties& properties,
133      const ScanImageCallback& callback) {
134    if (!file_descriptor->is_valid()) {
135      LOG(ERROR) << "Failed to scan image: file descriptor is invalid";
136      callback.Run(false);
137      return;
138    }
139    // Issue the dbus request to scan an image.
140    dbus::MethodCall method_call(
141        lorgnette::kManagerServiceInterface,
142        lorgnette::kScanImageMethod);
143    dbus::MessageWriter writer(&method_call);
144    writer.AppendString(device_name);
145    writer.AppendFileDescriptor(*file_descriptor);
146
147    dbus::MessageWriter option_writer(NULL);
148    dbus::MessageWriter element_writer(NULL);
149    writer.OpenArray("{sv}", &option_writer);
150    if (!properties.mode.empty()) {
151      option_writer.OpenDictEntry(&element_writer);
152      element_writer.AppendString(lorgnette::kScanPropertyMode);
153      element_writer.AppendVariantOfString(properties.mode);
154      option_writer.CloseContainer(&element_writer);
155    }
156    if (properties.resolution_dpi) {
157      option_writer.OpenDictEntry(&element_writer);
158      element_writer.AppendString(lorgnette::kScanPropertyResolution);
159      element_writer.AppendVariantOfUint32(properties.resolution_dpi);
160      option_writer.CloseContainer(&element_writer);
161    }
162    writer.CloseContainer(&option_writer);
163
164    lorgnette_daemon_proxy_->CallMethod(
165        &method_call,
166        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
167        base::Bind(&LorgnetteManagerClientImpl::OnScanImageComplete,
168                   weak_ptr_factory_.GetWeakPtr(),
169                   callback));
170  }
171
172  // Called when a response for ScanImage() is received.
173  void OnScanImageComplete(const ScanImageCallback& callback,
174                           dbus::Response* response) {
175    if (!response) {
176      LOG(ERROR) << "Failed to scan image";
177      callback.Run(false);
178      return;
179    }
180    callback.Run(true);
181  }
182
183  dbus::ObjectProxy* lorgnette_daemon_proxy_;
184  base::WeakPtrFactory<LorgnetteManagerClientImpl> weak_ptr_factory_;
185
186  DISALLOW_COPY_AND_ASSIGN(LorgnetteManagerClientImpl);
187};
188
189LorgnetteManagerClient::LorgnetteManagerClient() {
190}
191
192LorgnetteManagerClient::~LorgnetteManagerClient() {
193}
194
195// static
196LorgnetteManagerClient* LorgnetteManagerClient::Create() {
197  return new LorgnetteManagerClientImpl();
198}
199
200}  // namespace chromeos
201