OLD | NEW |
| (Empty) |
1 // Copyright 2017 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 <stdint.h> | |
6 | |
7 #include <memory> | |
8 #include <utility> | |
9 #include <vector> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/bind_helpers.h" | |
13 #include "base/macros.h" | |
14 #include "base/memory/ptr_util.h" | |
15 #include "base/observer_list_threadsafe.h" | |
16 #include "base/scoped_observer.h" | |
17 #include "base/strings/utf_string_conversions.h" | |
18 #include "base/synchronization/lock.h" | |
19 #include "base/threading/sequenced_task_runner_handle.h" | |
20 #include "chrome/browser/browser_process.h" | |
21 #include "chrome/browser/chromeos/printer_detector/printer_detector.h" | |
22 #include "chrome/browser/chromeos/printing/ppd_provider_factory.h" | |
23 #include "chrome/browser/chromeos/printing/printer_configurer.h" | |
24 #include "chrome/browser/chromeos/printing/printers_manager_factory.h" | |
25 #include "chrome/browser/chromeos/printing/usb_printer_util.h" | |
26 #include "chrome/browser/chromeos/profiles/profile_helper.h" | |
27 #include "chromeos/dbus/dbus_thread_manager.h" | |
28 #include "chromeos/dbus/debug_daemon_client.h" | |
29 #include "chromeos/printing/ppd_provider.h" | |
30 #include "content/public/browser/browser_thread.h" | |
31 #include "device/base/device_client.h" | |
32 #include "device/usb/usb_device.h" | |
33 #include "device/usb/usb_device_filter.h" | |
34 #include "device/usb/usb_service.h" | |
35 | |
36 namespace chromeos { | |
37 namespace { | |
38 | |
39 using printing::PpdProvider; | |
40 | |
41 // Aggregates the information needed for printer setup so it's easier to pass it | |
42 // around. | |
43 struct SetUpPrinterData { | |
44 // The configurer running the SetUpPrinter call. | |
45 std::unique_ptr<PrinterConfigurer> configurer; | |
46 | |
47 // The printer being set up. | |
48 std::unique_ptr<Printer> printer; | |
49 | |
50 // The usb device causing this setup flow. | |
51 scoped_refptr<device::UsbDevice> device; | |
52 | |
53 // True if this printer is one that the user doesn't already have a | |
54 // configuration for. | |
55 bool is_new; | |
56 | |
57 // True if this was a printer that was plugged in during the session, false if | |
58 // it was already plugged in when the session started. | |
59 bool hotplugged; | |
60 }; | |
61 | |
62 // Given a usb device, guesses the make and model for a driver lookup. | |
63 // | |
64 // TODO(justincarlson): Possibly go deeper and query the IEEE1284 fields | |
65 // for make and model if we determine those are more likely to contain | |
66 // what we want. | |
67 std::string GuessEffectiveMakeAndModel(const device::UsbDevice& device) { | |
68 return base::UTF16ToUTF8(device.manufacturer_string()) + " " + | |
69 base::UTF16ToUTF8(device.product_string()); | |
70 } | |
71 | |
72 // The PrinterDetector that drives the flow for setting up a USB printer to use | |
73 // CUPS backend. | |
74 class CupsPrinterDetectorImpl : public PrinterDetector, | |
75 public device::UsbService::Observer { | |
76 public: | |
77 explicit CupsPrinterDetectorImpl(Profile* profile) | |
78 : profile_(profile), | |
79 usb_observer_(this), | |
80 observer_list_( | |
81 new base::ObserverListThreadSafe<PrinterDetector::Observer>), | |
82 weak_ptr_factory_(this) { | |
83 device::UsbService* usb_service = | |
84 device::DeviceClient::Get()->GetUsbService(); | |
85 if (usb_service) { | |
86 usb_observer_.Add(usb_service); | |
87 usb_service->GetDevices(base::Bind(&CupsPrinterDetectorImpl::OnGetDevices, | |
88 weak_ptr_factory_.GetWeakPtr())); | |
89 } | |
90 } | |
91 ~CupsPrinterDetectorImpl() override = default; | |
92 | |
93 // PrinterDetector interface function. | |
94 void AddObserver(PrinterDetector::Observer* observer) override { | |
95 observer_list_->AddObserver(observer); | |
96 } | |
97 | |
98 // PrinterDetector interface function. | |
99 void RemoveObserver(PrinterDetector::Observer* observer) override { | |
100 observer_list_->RemoveObserver(observer); | |
101 } | |
102 | |
103 // PrinterDetector interface function. | |
104 std::vector<Printer> GetPrinters() override { | |
105 base::AutoLock auto_lock(pp_lock_); | |
106 return GetPrintersLocked(); | |
107 } | |
108 | |
109 private: | |
110 std::vector<Printer> GetPrintersLocked() { | |
111 pp_lock_.AssertAcquired(); | |
112 std::vector<Printer> printers; | |
113 printers.reserve(present_printers_.size()); | |
114 for (const auto& entry : present_printers_) { | |
115 printers.push_back(*entry.second); | |
116 } | |
117 return printers; | |
118 } | |
119 | |
120 // Callback for initial enumeration of usb devices. | |
121 void OnGetDevices( | |
122 const std::vector<scoped_refptr<device::UsbDevice>>& devices) { | |
123 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
124 for (const auto& device : devices) { | |
125 MaybeSetUpDevice(device, false); | |
126 } | |
127 } | |
128 | |
129 // UsbService::observer override. | |
130 void OnDeviceAdded(scoped_refptr<device::UsbDevice> device) override { | |
131 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
132 MaybeSetUpDevice(device, true); | |
133 } | |
134 | |
135 // UsbService::observer override. | |
136 void OnDeviceRemoved(scoped_refptr<device::UsbDevice> device) override { | |
137 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
138 if (!UsbDeviceIsPrinter(*device)) { | |
139 return; | |
140 } | |
141 | |
142 base::AutoLock auto_lock(pp_lock_); | |
143 if (base::ContainsKey(present_printers_, device->guid())) { | |
144 present_printers_.erase(device->guid()); | |
145 auto printers = GetPrintersLocked(); | |
146 // We already have pp_lock_, so need to call the pre-locked version of | |
147 // GetPrinters to prevent deadlock. | |
148 observer_list_->Notify( | |
149 FROM_HERE, &PrinterDetector::Observer::OnAvailableUsbPrintersChanged, | |
150 GetPrintersLocked()); | |
151 } else { | |
152 // If the device has been removed but it's not in present_printers_, it | |
153 // must still be in the setup flow. | |
154 deferred_printer_removals_.insert(device->guid()); | |
155 } | |
156 } | |
157 | |
158 // If this device is a printer and we haven't already tried to set it up, | |
159 // starts the process of setting the printer up. |hotplugged| | |
160 // should be true if this was plugged in during the session. | |
161 void MaybeSetUpDevice(scoped_refptr<device::UsbDevice> device, | |
162 bool hotplugged) { | |
163 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
164 | |
165 if (!UsbDeviceIsPrinter(*device)) { | |
166 return; | |
167 } | |
168 | |
169 // If we got this far, we want to try to auto-configure this printer. | |
170 auto data = base::MakeUnique<SetUpPrinterData>(); | |
171 data->configurer = PrinterConfigurer::Create(profile_); | |
172 data->device = device; | |
173 data->hotplugged = hotplugged; | |
174 | |
175 data->printer = UsbDeviceToPrinter(*device); | |
176 if (data->printer == nullptr) { | |
177 // We've failed to understand this printer device, an error will already | |
178 // have been logged, so just bail. | |
179 return; | |
180 } | |
181 | |
182 // If the user already has a configuration for this device, substitute that | |
183 // one for the one we generated automatically and skip the parts where we | |
184 // try to automagically figure out the driver. | |
185 std::unique_ptr<Printer> existing_printer_configuration = | |
186 PrintersManagerFactory::GetForBrowserContext(profile_)->GetPrinter( | |
187 data->printer->id()); | |
188 if (existing_printer_configuration != nullptr) { | |
189 data->is_new = false; | |
190 data->printer = std::move(existing_printer_configuration); | |
191 OnPrinterResolved(std::move(data)); | |
192 return; | |
193 } | |
194 | |
195 // It's not a device we have configured previously. | |
196 // | |
197 // TODO(justincarlson): Add a notification that we are attempting to set up | |
198 // this printer at this point. | |
199 data->is_new = true; | |
200 | |
201 // Look for an exact match based on USB ids. | |
202 scoped_refptr<PpdProvider> ppd_provider = | |
203 printing::CreateProvider(profile_); | |
204 ppd_provider->ResolveUsbIds( | |
205 device->vendor_id(), device->product_id(), | |
206 base::Bind(&CupsPrinterDetectorImpl::ResolveUsbIdsDone, | |
207 weak_ptr_factory_.GetWeakPtr(), ppd_provider, | |
208 base::Passed(std::move(data)))); | |
209 } | |
210 | |
211 void OnPrinterResolved(std::unique_ptr<SetUpPrinterData> data) { | |
212 // |data| will be invalidated by the move below, so we have to latch it | |
213 // before the call. | |
214 SetUpPrinterData* data_ptr = data.get(); | |
215 data_ptr->configurer->SetUpPrinter( | |
216 *(data_ptr->printer), | |
217 base::Bind(&CupsPrinterDetectorImpl::SetUpPrinterDone, | |
218 weak_ptr_factory_.GetWeakPtr(), | |
219 base::Passed(std::move(data)))); | |
220 } | |
221 | |
222 // Called when the query for a driver based on usb ids completes. | |
223 // | |
224 // Note |provider| is not used in this function, it's just passed along to | |
225 // keep it alive during the USB resolution. | |
226 void ResolveUsbIdsDone(scoped_refptr<PpdProvider> provider, | |
227 std::unique_ptr<SetUpPrinterData> data, | |
228 PpdProvider::CallbackResultCode result, | |
229 const std::string& effective_make_and_model) { | |
230 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
231 if (result == PpdProvider::SUCCESS) { | |
232 // Got something based on usb ids. Go with it. | |
233 data->printer->mutable_ppd_reference()->effective_make_and_model = | |
234 effective_make_and_model; | |
235 } else { | |
236 // Couldn't figure this printer out based on usb ids, fall back to | |
237 // guessing the make/model string from what the USB system reports. | |
238 // | |
239 // TODO(justincarlson): Consider adding a mechanism for aggregating data | |
240 // about which usb devices are in the wild but unsupported? | |
241 data->printer->mutable_ppd_reference()->effective_make_and_model = | |
242 GuessEffectiveMakeAndModel(*data->device); | |
243 } | |
244 OnPrinterResolved(std::move(data)); | |
245 } | |
246 | |
247 // Called with the result of asking to have a printer configured for CUPS. If | |
248 // |printer_to_register| is non-null and we successfully configured, then the | |
249 // printer is registered with the printers manager. | |
250 void SetUpPrinterDone(std::unique_ptr<SetUpPrinterData> data, | |
251 PrinterSetupResult result) { | |
252 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
253 if (result == PrinterSetupResult::kSuccess) { | |
254 if (data->is_new) { | |
255 // We aren't done with data->printer yet, so we have to copy it instead | |
256 // of moving it. | |
257 auto printer_copy = base::MakeUnique<Printer>(*data->printer); | |
258 PrintersManagerFactory::GetForBrowserContext(profile_)->RegisterPrinter( | |
259 std::move(printer_copy)); | |
260 } | |
261 // TODO(justincarlson): If the device was hotplugged, pop a timed | |
262 // notification that says the printer is now available for printing. | |
263 } | |
264 // TODO(justincarlson): If this doesn't succeed, pop a notification that | |
265 // tells the user automatic setup failed and offers to open the CUPS printer | |
266 // configuration settings. | |
267 | |
268 if (base::ContainsKey(deferred_printer_removals_, data->device->guid())) { | |
269 // The device was removed before we finished the flow, so just don't add | |
270 // it to present_printers_; | |
271 deferred_printer_removals_.erase(data->device->guid()); | |
272 } else { | |
273 base::AutoLock auto_lock(pp_lock_); | |
274 present_printers_.emplace(data->device->guid(), std::move(data->printer)); | |
275 observer_list_->Notify( | |
276 FROM_HERE, &PrinterDetector::Observer::OnAvailableUsbPrintersChanged, | |
277 GetPrintersLocked()); | |
278 } | |
279 } | |
280 | |
281 void SetNotificationUIManagerForTesting( | |
282 NotificationUIManager* manager) override { | |
283 LOG(FATAL) << "Not implemented for CUPS"; | |
284 } | |
285 | |
286 // Map from USB GUID to Printer that we have detected as being currently | |
287 // plugged in and have finished processing. Note present_printers_ may be | |
288 // accessed from multiple threads, so is protected by pp_lock_. | |
289 std::map<std::string, std::unique_ptr<Printer>> present_printers_; | |
290 base::Lock pp_lock_; | |
291 | |
292 // If the usb device is removed before we've finished processing it, we'll | |
293 // defer the cleanup until the setup flow finishes. This is the set of | |
294 // guids which have been removed before the flow finished. | |
295 std::set<std::string> deferred_printer_removals_; | |
296 | |
297 Profile* profile_; | |
298 ScopedObserver<device::UsbService, device::UsbService::Observer> | |
299 usb_observer_; | |
300 scoped_refptr<base::ObserverListThreadSafe<PrinterDetector::Observer>> | |
301 observer_list_; | |
302 base::WeakPtrFactory<CupsPrinterDetectorImpl> weak_ptr_factory_; | |
303 }; | |
304 | |
305 } // namespace | |
306 | |
307 // Nop base class implementation of GetPrinters(). Because this is non-empty we | |
308 // have to define it out-of-line. | |
309 std::vector<Printer> PrinterDetector::GetPrinters() { | |
310 return std::vector<Printer>(); | |
311 } | |
312 | |
313 // static | |
314 std::unique_ptr<PrinterDetector> PrinterDetector::CreateCups(Profile* profile) { | |
315 return base::MakeUnique<CupsPrinterDetectorImpl>(profile); | |
316 } | |
317 | |
318 } // namespace chromeos | |
OLD | NEW |