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; | |
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 observer_list_->Notify( |
116 // https://crbug.com/700602 | 147 FROM_HERE, &PrinterDetector::Observer::OnAvailableUsbPrintersChanged, |
skau
2017/03/31 17:50:11
Add a note to this method that calling GetPrinters
Carlson
2017/03/31 18:20:06
Done. (I really, really wish we had lock annotati
| |
117 auto existing_printers = | 148 GetPrintersLocked()); |
118 PrintersManagerFactory::GetForBrowserContext(profile_)->GetPrinters(); | 149 } else { |
119 for (std::unique_ptr<Printer>& printer : existing_printers) { | 150 // If the device has been removed but it's not in present_printers_, it |
120 if (printer->uri() == uri) { | 151 // must still be in the setup flow. |
121 // Found a match, so use the existing configuration. | 152 deferred_printer_removals_.insert(device->guid()); |
122 return std::move(printer); | |
123 } | |
124 } | 153 } |
125 return nullptr; | |
126 } | 154 } |
127 | 155 |
128 // If this device is a printer and we haven't already tried to set it up, | 156 // 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| | 157 // starts the process of setting the printer up. |hotplugged| |
130 // should be true if this was plugged in during the session. | 158 // should be true if this was plugged in during the session. |
131 void MaybeSetUpDevice(scoped_refptr<device::UsbDevice> device, | 159 void MaybeSetUpDevice(scoped_refptr<device::UsbDevice> device, |
132 bool hotplugged) { | 160 bool hotplugged) { |
133 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 161 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
134 | 162 |
135 if (!UsbDeviceIsPrinter(*device) || | 163 if (!UsbDeviceIsPrinter(*device)) { |
136 base::ContainsKey(known_printers_, device->guid())) { | |
137 return; | 164 return; |
138 } | 165 } |
139 known_printers_.insert(device->guid()); | |
140 | 166 |
167 // If we got this far, we want to try to auto-configure this printer. | |
141 auto data = base::MakeUnique<SetUpPrinterData>(); | 168 auto data = base::MakeUnique<SetUpPrinterData>(); |
142 data->configurer = PrinterConfigurer::Create(profile_); | 169 data->configurer = PrinterConfigurer::Create(profile_); |
143 data->device = device; | 170 data->device = device; |
144 data->hotplugged = hotplugged; | 171 data->hotplugged = hotplugged; |
145 | 172 |
146 data->printer = FindExistingPrinter(UsbPrinterUri(*device)); | 173 data->printer = UsbDeviceToPrinter(*device); |
147 if (data->printer != nullptr) { | 174 if (data->printer == nullptr) { |
175 // We've failed to understand this printer device, an error will already | |
176 // have been logged, so just bail. | |
177 return; | |
178 } | |
179 | |
180 // If the user already has a configuration for this device, substitute that | |
181 // one for the one we generated automatically and skip the parts where we | |
182 // try to automagically figure out the driver. | |
183 auto existing_printer_configuration = | |
184 PrintersManagerFactory::GetForBrowserContext(profile_)->GetPrinter( | |
185 data->printer->id()); | |
186 if (existing_printer_configuration != nullptr) { | |
148 data->is_new = false; | 187 data->is_new = false; |
188 data->printer = std::move(existing_printer_configuration); | |
149 OnPrinterResolved(std::move(data)); | 189 OnPrinterResolved(std::move(data)); |
150 return; | 190 return; |
151 } | 191 } |
152 | 192 |
153 // It's not a device we have configured previously. | 193 // It's not a device we have configured previously. |
154 // | 194 // |
155 // TODO(justincarlson): Add a notification that we are attempting to set up | 195 // TODO(justincarlson): Add a notification that we are attempting to set up |
156 // this printer at this point. | 196 // this printer at this point. |
157 data->is_new = true; | 197 data->is_new = true; |
158 data->printer = UsbDeviceToPrinter(*device); | |
159 if (data->printer == nullptr) { | |
160 return; | |
161 } | |
162 | 198 |
163 // Look for an exact match based on USB ids. | 199 // Look for an exact match based on USB ids. |
164 scoped_refptr<PpdProvider> ppd_provider = | 200 scoped_refptr<PpdProvider> ppd_provider = |
165 printing::CreateProvider(profile_); | 201 printing::CreateProvider(profile_); |
166 ppd_provider->ResolveUsbIds( | 202 ppd_provider->ResolveUsbIds( |
167 device->vendor_id(), device->product_id(), | 203 device->vendor_id(), device->product_id(), |
168 base::Bind(&CupsPrinterDetectorImpl::ResolveUsbIdsDone, | 204 base::Bind(&CupsPrinterDetectorImpl::ResolveUsbIdsDone, |
169 weak_ptr_factory_.GetWeakPtr(), ppd_provider, | 205 weak_ptr_factory_.GetWeakPtr(), ppd_provider, |
170 base::Passed(std::move(data)))); | 206 base::Passed(std::move(data)))); |
171 } | 207 } |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
207 } | 243 } |
208 | 244 |
209 // Called with the result of asking to have a printer configured for CUPS. If | 245 // 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 | 246 // |printer_to_register| is non-null and we successfully configured, then the |
211 // printer is registered with the printers manager. | 247 // printer is registered with the printers manager. |
212 void SetUpPrinterDone(std::unique_ptr<SetUpPrinterData> data, | 248 void SetUpPrinterDone(std::unique_ptr<SetUpPrinterData> data, |
213 PrinterSetupResult result) { | 249 PrinterSetupResult result) { |
214 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 250 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
215 if (result == PrinterSetupResult::SUCCESS) { | 251 if (result == PrinterSetupResult::SUCCESS) { |
216 if (data->is_new) { | 252 if (data->is_new) { |
253 // We aren't done with data->printer yet, so we have to copy it instead | |
254 // of moving it. | |
255 auto printer_copy = base::MakeUnique<Printer>(); | |
256 *printer_copy = *data->printer; | |
217 PrintersManagerFactory::GetForBrowserContext(profile_)->RegisterPrinter( | 257 PrintersManagerFactory::GetForBrowserContext(profile_)->RegisterPrinter( |
218 std::move(data->printer)); | 258 std::move(printer_copy)); |
219 } | 259 } |
220 // TODO(justincarlson): If the device was hotplugged, pop a timed | 260 // TODO(justincarlson): If the device was hotplugged, pop a timed |
221 // notification that says the printer is now available for printing. | 261 // notification that says the printer is now available for printing. |
222 } | 262 } |
223 // TODO(justincarlson): If this doesn't succeed, Pop a notification that | 263 // 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 | 264 // tells the user automatic setup failed and offers to open the CUPS printer |
225 // configuration settings. | 265 // configuration settings. |
226 // | 266 |
227 // TODO(justincarlson): If this doesn't succeed, Update the list of printers | 267 if (base::ContainsKey(deferred_printer_removals_, data->device->guid())) { |
228 // that failed to set up. | 268 // The device was removed before we finished the flow, so just don't add |
269 // it to present_printers_; | |
270 deferred_printer_removals_.erase(data->device->guid()); | |
271 } else { | |
272 base::AutoLock auto_lock(pp_lock_); | |
273 present_printers_.emplace(data->device->guid(), std::move(data->printer)); | |
274 observer_list_->Notify( | |
275 FROM_HERE, &PrinterDetector::Observer::OnAvailableUsbPrintersChanged, | |
276 GetPrintersLocked()); | |
277 } | |
229 } | 278 } |
230 | 279 |
231 void SetNotificationUIManagerForTesting( | 280 void SetNotificationUIManagerForTesting( |
232 NotificationUIManager* manager) override { | 281 NotificationUIManager* manager) override { |
233 LOG(FATAL) << "Not implemented for CUPS"; | 282 LOG(FATAL) << "Not implemented for CUPS"; |
234 } | 283 } |
235 | 284 |
236 // USB GUIDs of printers we've already dealt with. There's an inherent race | 285 // Map from USB GUID to Printer that we have detected as being currently |
237 // between initially querying all usb devices and receiving a notification | 286 // 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 | 287 // accessed from multiple threads, so is protected by pp_lock_. |
239 // printer exactly once. | 288 std::map<std::string, std::unique_ptr<Printer>> present_printers_; |
240 std::set<std::string> known_printers_; | 289 base::Lock pp_lock_; |
290 | |
291 // If the usb device is removed before we've finished processing it, we'll | |
292 // defer the cleanup until the setup flow finishes. This is the set of | |
293 // guids which have been removed before the flow finished. | |
294 std::set<std::string> deferred_printer_removals_; | |
241 | 295 |
242 Profile* profile_; | 296 Profile* profile_; |
243 ScopedObserver<device::UsbService, device::UsbService::Observer> observer_; | 297 ScopedObserver<device::UsbService, device::UsbService::Observer> |
298 usb_observer_; | |
299 scoped_refptr<base::ObserverListThreadSafe<PrinterDetector::Observer>> | |
300 observer_list_; | |
244 base::WeakPtrFactory<CupsPrinterDetectorImpl> weak_ptr_factory_; | 301 base::WeakPtrFactory<CupsPrinterDetectorImpl> weak_ptr_factory_; |
245 }; | 302 }; |
246 | 303 |
247 } // namespace | 304 } // namespace |
248 | 305 |
249 // static | 306 // static |
250 std::unique_ptr<PrinterDetector> PrinterDetector::CreateCups(Profile* profile) { | 307 std::unique_ptr<PrinterDetector> PrinterDetector::CreateCups(Profile* profile) { |
251 return base::MakeUnique<CupsPrinterDetectorImpl>(profile); | 308 return base::MakeUnique<CupsPrinterDetectorImpl>(profile); |
252 } | 309 } |
253 | 310 |
254 } // namespace chromeos | 311 } // namespace chromeos |
OLD | NEW |