OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |