Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(21)

Side by Side Diff: chrome/browser/chromeos/printer_detector/cups_printer_detector.cc

Issue 2790603003: Make CUPS USB printing play better with the settings page. This change does several things: (Closed)
Patch Set: Address xdai@ comments Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <stdint.h> 5 #include <stdint.h>
6 6
7 #include <memory> 7 #include <memory>
8 #include <utility> 8 #include <utility>
9 #include <vector> 9 #include <vector>
10 10
11 #include "base/bind.h" 11 #include "base/bind.h"
12 #include "base/bind_helpers.h" 12 #include "base/bind_helpers.h"
13 #include "base/macros.h" 13 #include "base/macros.h"
14 #include "base/memory/ptr_util.h" 14 #include "base/memory/ptr_util.h"
15 #include "base/observer_list_threadsafe.h"
15 #include "base/scoped_observer.h" 16 #include "base/scoped_observer.h"
16 #include "base/strings/utf_string_conversions.h" 17 #include "base/strings/utf_string_conversions.h"
18 #include "base/synchronization/lock.h"
19 #include "base/threading/sequenced_task_runner_handle.h"
17 #include "chrome/browser/browser_process.h" 20 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/chromeos/printer_detector/printer_detector.h" 21 #include "chrome/browser/chromeos/printer_detector/printer_detector.h"
19 #include "chrome/browser/chromeos/printing/ppd_provider_factory.h" 22 #include "chrome/browser/chromeos/printing/ppd_provider_factory.h"
20 #include "chrome/browser/chromeos/printing/printer_configurer.h" 23 #include "chrome/browser/chromeos/printing/printer_configurer.h"
21 #include "chrome/browser/chromeos/printing/printers_manager_factory.h" 24 #include "chrome/browser/chromeos/printing/printers_manager_factory.h"
22 #include "chrome/browser/chromeos/printing/usb_printer_util.h" 25 #include "chrome/browser/chromeos/printing/usb_printer_util.h"
23 #include "chrome/browser/chromeos/profiles/profile_helper.h" 26 #include "chrome/browser/chromeos/profiles/profile_helper.h"
24 #include "chromeos/dbus/dbus_thread_manager.h" 27 #include "chromeos/dbus/dbus_thread_manager.h"
25 #include "chromeos/dbus/debug_daemon_client.h" 28 #include "chromeos/dbus/debug_daemon_client.h"
26 #include "chromeos/printing/ppd_provider.h" 29 #include "chromeos/printing/ppd_provider.h"
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
65 return base::UTF16ToUTF8(device.manufacturer_string()) + " " + 68 return base::UTF16ToUTF8(device.manufacturer_string()) + " " +
66 base::UTF16ToUTF8(device.product_string()); 69 base::UTF16ToUTF8(device.product_string());
67 } 70 }
68 71
69 // The PrinterDetector that drives the flow for setting up a USB printer to use 72 // The PrinterDetector that drives the flow for setting up a USB printer to use
70 // CUPS backend. 73 // CUPS backend.
71 class CupsPrinterDetectorImpl : public PrinterDetector, 74 class CupsPrinterDetectorImpl : public PrinterDetector,
72 public device::UsbService::Observer { 75 public device::UsbService::Observer {
73 public: 76 public:
74 explicit CupsPrinterDetectorImpl(Profile* profile) 77 explicit CupsPrinterDetectorImpl(Profile* profile)
75 : profile_(profile), observer_(this), weak_ptr_factory_(this) { 78 : profile_(profile),
79 usb_observer_(this),
80 observer_list_(
81 new base::ObserverListThreadSafe<PrinterDetector::Observer>),
82 weak_ptr_factory_(this) {
76 device::UsbService* usb_service = 83 device::UsbService* usb_service =
77 device::DeviceClient::Get()->GetUsbService(); 84 device::DeviceClient::Get()->GetUsbService();
78 if (usb_service) { 85 if (usb_service) {
79 observer_.Add(usb_service); 86 usb_observer_.Add(usb_service);
80 usb_service->GetDevices(base::Bind(&CupsPrinterDetectorImpl::OnGetDevices, 87 usb_service->GetDevices(base::Bind(&CupsPrinterDetectorImpl::OnGetDevices,
81 weak_ptr_factory_.GetWeakPtr())); 88 weak_ptr_factory_.GetWeakPtr()));
82 } 89 }
83 } 90 }
84 ~CupsPrinterDetectorImpl() override = default; 91 ~CupsPrinterDetectorImpl() override = default;
85 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 auto ret = GetPrintersLocked();
107 return ret;
stevenjb 2017/04/10 19:49:55 local not needed. Also, we could avoid an extra c
Carlson 2017/04/10 22:16:41 Removed temporary. RE: efficiency, is there a rea
108 }
109
86 private: 110 private:
111 std::vector<Printer> GetPrintersLocked() {
112 pp_lock_.AssertAcquired();
113 std::vector<Printer> printers;
114 printers.reserve(present_printers_.size());
115 for (const auto& entry : present_printers_) {
116 printers.push_back(*entry.second);
117 }
118 return printers;
119 }
120
87 // Callback for initial enumeration of usb devices. 121 // Callback for initial enumeration of usb devices.
88 void OnGetDevices( 122 void OnGetDevices(
89 const std::vector<scoped_refptr<device::UsbDevice>>& devices) { 123 const std::vector<scoped_refptr<device::UsbDevice>>& devices) {
90 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 124 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
91 for (const auto& device : devices) { 125 for (const auto& device : devices) {
92 MaybeSetUpDevice(device, false); 126 MaybeSetUpDevice(device, false);
93 } 127 }
94 } 128 }
95 129
96 // UsbService::observer override. 130 // UsbService::observer override.
97 void OnDeviceAdded(scoped_refptr<device::UsbDevice> device) override { 131 void OnDeviceAdded(scoped_refptr<device::UsbDevice> device) override {
98 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 132 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
99 MaybeSetUpDevice(device, true); 133 MaybeSetUpDevice(device, true);
100 } 134 }
101 135
102 // UsbService::observer override. 136 // UsbService::observer override.
103 void OnDeviceRemoved(scoped_refptr<device::UsbDevice> device) override { 137 void OnDeviceRemoved(scoped_refptr<device::UsbDevice> device) override {
104 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 138 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
105 if (!UsbDeviceIsPrinter(*device)) { 139 if (!UsbDeviceIsPrinter(*device)) {
106 return; 140 return;
107 } 141 }
108 known_printers_.erase(device->guid());
109 // TODO(justincarlson): Update failed printers.
110 }
111 142
112 // Returns the existing printer using this URI, if one exists, or 143 base::AutoLock auto_lock(pp_lock_);
113 // null otherwise. 144 if (base::ContainsKey(present_printers_, device->guid())) {
114 std::unique_ptr<Printer> FindExistingPrinter(const std::string& uri) { 145 present_printers_.erase(device->guid());
115 // TODO(justincarlson): add a GetPrinterByURI to PrintersManager. 146 // We already have pp_lock_, so need to call the pre-locked version of
116 // https://crbug.com/700602 147 // GetPrinters to prevent deadlock.
117 auto existing_printers = 148 observer_list_->Notify(
118 PrintersManagerFactory::GetForBrowserContext(profile_)->GetPrinters(); 149 FROM_HERE, &PrinterDetector::Observer::OnAvailableUsbPrintersChanged,
119 for (std::unique_ptr<Printer>& printer : existing_printers) { 150 GetPrintersLocked());
stevenjb 2017/04/10 19:49:55 Each call to Notify is going to build a new vector
Carlson 2017/04/10 22:16:41 I don't understand your concern here, sorry. Ther
stevenjb 2017/04/11 16:27:42 Apologies I was somehow thinking this was inside a
120 if (printer->uri() == uri) { 151 } else {
121 // Found a match, so use the existing configuration. 152 // If the device has been removed but it's not in present_printers_, it
122 return std::move(printer); 153 // must still be in the setup flow.
123 } 154 deferred_printer_removals_.insert(device->guid());
124 } 155 }
125 return nullptr;
126 } 156 }
127 157
128 // If this device is a printer and we haven't already tried to set it up, 158 // If this device is a printer and we haven't already tried to set it up,
129 // starts the process of setting the printer up. |hotplugged| 159 // starts the process of setting the printer up. |hotplugged|
130 // should be true if this was plugged in during the session. 160 // should be true if this was plugged in during the session.
131 void MaybeSetUpDevice(scoped_refptr<device::UsbDevice> device, 161 void MaybeSetUpDevice(scoped_refptr<device::UsbDevice> device,
132 bool hotplugged) { 162 bool hotplugged) {
133 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 163 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
134 164
135 if (!UsbDeviceIsPrinter(*device) || 165 if (!UsbDeviceIsPrinter(*device)) {
136 base::ContainsKey(known_printers_, device->guid())) {
137 return; 166 return;
138 } 167 }
139 known_printers_.insert(device->guid());
140 168
169 // If we got this far, we want to try to auto-configure this printer.
141 auto data = base::MakeUnique<SetUpPrinterData>(); 170 auto data = base::MakeUnique<SetUpPrinterData>();
142 data->configurer = PrinterConfigurer::Create(profile_); 171 data->configurer = PrinterConfigurer::Create(profile_);
143 data->device = device; 172 data->device = device;
144 data->hotplugged = hotplugged; 173 data->hotplugged = hotplugged;
145 174
146 data->printer = FindExistingPrinter(UsbPrinterUri(*device)); 175 data->printer = UsbDeviceToPrinter(*device);
147 if (data->printer != nullptr) { 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 auto existing_printer_configuration =
stevenjb 2017/04/10 19:49:55 Avoid using auto when the return type is not obvio
Carlson 2017/04/10 22:16:41 Done.
186 PrintersManagerFactory::GetForBrowserContext(profile_)->GetPrinter(
187 data->printer->id());
188 if (existing_printer_configuration != nullptr) {
148 data->is_new = false; 189 data->is_new = false;
190 data->printer = std::move(existing_printer_configuration);
149 OnPrinterResolved(std::move(data)); 191 OnPrinterResolved(std::move(data));
150 return; 192 return;
151 } 193 }
152 194
153 // It's not a device we have configured previously. 195 // It's not a device we have configured previously.
154 // 196 //
155 // TODO(justincarlson): Add a notification that we are attempting to set up 197 // TODO(justincarlson): Add a notification that we are attempting to set up
156 // this printer at this point. 198 // this printer at this point.
157 data->is_new = true; 199 data->is_new = true;
158 data->printer = UsbDeviceToPrinter(*device);
159 if (data->printer == nullptr) {
160 return;
161 }
162 200
163 // Look for an exact match based on USB ids. 201 // Look for an exact match based on USB ids.
164 scoped_refptr<PpdProvider> ppd_provider = 202 scoped_refptr<PpdProvider> ppd_provider =
165 printing::CreateProvider(profile_); 203 printing::CreateProvider(profile_);
166 ppd_provider->ResolveUsbIds( 204 ppd_provider->ResolveUsbIds(
167 device->vendor_id(), device->product_id(), 205 device->vendor_id(), device->product_id(),
168 base::Bind(&CupsPrinterDetectorImpl::ResolveUsbIdsDone, 206 base::Bind(&CupsPrinterDetectorImpl::ResolveUsbIdsDone,
169 weak_ptr_factory_.GetWeakPtr(), ppd_provider, 207 weak_ptr_factory_.GetWeakPtr(), ppd_provider,
170 base::Passed(std::move(data)))); 208 base::Passed(std::move(data))));
171 } 209 }
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
207 } 245 }
208 246
209 // Called with the result of asking to have a printer configured for CUPS. If 247 // Called with the result of asking to have a printer configured for CUPS. If
210 // |printer_to_register| is non-null and we successfully configured, then the 248 // |printer_to_register| is non-null and we successfully configured, then the
211 // printer is registered with the printers manager. 249 // printer is registered with the printers manager.
212 void SetUpPrinterDone(std::unique_ptr<SetUpPrinterData> data, 250 void SetUpPrinterDone(std::unique_ptr<SetUpPrinterData> data,
213 PrinterSetupResult result) { 251 PrinterSetupResult result) {
214 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 252 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
215 if (result == PrinterSetupResult::SUCCESS) { 253 if (result == PrinterSetupResult::SUCCESS) {
216 if (data->is_new) { 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>();
258 *printer_copy = *data->printer;
stevenjb 2017/04/10 19:49:55 I think this would be more clear as: auto printer_
Carlson 2017/04/10 22:16:41 Don't need the "new Printer" part, but otherwise,
stevenjb 2017/04/11 16:27:42 Acknowledged.
217 PrintersManagerFactory::GetForBrowserContext(profile_)->RegisterPrinter( 259 PrintersManagerFactory::GetForBrowserContext(profile_)->RegisterPrinter(
218 std::move(data->printer)); 260 std::move(printer_copy));
219 } 261 }
220 // TODO(justincarlson): If the device was hotplugged, pop a timed 262 // TODO(justincarlson): If the device was hotplugged, pop a timed
221 // notification that says the printer is now available for printing. 263 // notification that says the printer is now available for printing.
222 } 264 }
223 // TODO(justincarlson): If this doesn't succeed, Pop a notification that 265 // TODO(justincarlson): If this doesn't succeed, pop a notification that
224 // tells the user automatic setup failed and offers to open the CUPS printer 266 // tells the user automatic setup failed and offers to open the CUPS printer
225 // configuration settings. 267 // configuration settings.
226 // 268
227 // TODO(justincarlson): If this doesn't succeed, Update the list of printers 269 if (base::ContainsKey(deferred_printer_removals_, data->device->guid())) {
228 // that failed to set up. 270 // The device was removed before we finished the flow, so just don't add
271 // it to present_printers_;
272 deferred_printer_removals_.erase(data->device->guid());
273 } else {
274 base::AutoLock auto_lock(pp_lock_);
275 present_printers_.emplace(data->device->guid(), std::move(data->printer));
276 observer_list_->Notify(
277 FROM_HERE, &PrinterDetector::Observer::OnAvailableUsbPrintersChanged,
278 GetPrintersLocked());
279 }
229 } 280 }
230 281
231 void SetNotificationUIManagerForTesting( 282 void SetNotificationUIManagerForTesting(
232 NotificationUIManager* manager) override { 283 NotificationUIManager* manager) override {
233 LOG(FATAL) << "Not implemented for CUPS"; 284 LOG(FATAL) << "Not implemented for CUPS";
234 } 285 }
235 286
236 // USB GUIDs of printers we've already dealt with. There's an inherent race 287 // Map from USB GUID to Printer that we have detected as being currently
237 // between initially querying all usb devices and receiving a notification 288 // plugged in and have finished processing. Note present_printers_ may be
238 // about a new device, so this set lets us guarantee that we handle a given 289 // accessed from multiple threads, so is protected by pp_lock_.
239 // printer exactly once. 290 std::map<std::string, std::unique_ptr<Printer>> present_printers_;
240 std::set<std::string> known_printers_; 291 base::Lock pp_lock_;
292
293 // If the usb device is removed before we've finished processing it, we'll
294 // defer the cleanup until the setup flow finishes. This is the set of
295 // guids which have been removed before the flow finished.
296 std::set<std::string> deferred_printer_removals_;
241 297
242 Profile* profile_; 298 Profile* profile_;
243 ScopedObserver<device::UsbService, device::UsbService::Observer> observer_; 299 ScopedObserver<device::UsbService, device::UsbService::Observer>
300 usb_observer_;
301 scoped_refptr<base::ObserverListThreadSafe<PrinterDetector::Observer>>
302 observer_list_;
244 base::WeakPtrFactory<CupsPrinterDetectorImpl> weak_ptr_factory_; 303 base::WeakPtrFactory<CupsPrinterDetectorImpl> weak_ptr_factory_;
245 }; 304 };
246 305
247 } // namespace 306 } // namespace
248 307
249 // static 308 // static
250 std::unique_ptr<PrinterDetector> PrinterDetector::CreateCups(Profile* profile) { 309 std::unique_ptr<PrinterDetector> PrinterDetector::CreateCups(Profile* profile) {
251 return base::MakeUnique<CupsPrinterDetectorImpl>(profile); 310 return base::MakeUnique<CupsPrinterDetectorImpl>(profile);
252 } 311 }
253 312
254 } // namespace chromeos 313 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698