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 return GetPrintersLocked(); |
| 107 } |
| 108 |
86 private: | 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 |
87 // Callback for initial enumeration of usb devices. | 120 // Callback for initial enumeration of usb devices. |
88 void OnGetDevices( | 121 void OnGetDevices( |
89 const std::vector<scoped_refptr<device::UsbDevice>>& devices) { | 122 const std::vector<scoped_refptr<device::UsbDevice>>& devices) { |
90 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 123 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
91 for (const auto& device : devices) { | 124 for (const auto& device : devices) { |
92 MaybeSetUpDevice(device, false); | 125 MaybeSetUpDevice(device, false); |
93 } | 126 } |
94 } | 127 } |
95 | 128 |
96 // UsbService::observer override. | 129 // UsbService::observer override. |
97 void OnDeviceAdded(scoped_refptr<device::UsbDevice> device) override { | 130 void OnDeviceAdded(scoped_refptr<device::UsbDevice> device) override { |
98 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 131 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
99 MaybeSetUpDevice(device, true); | 132 MaybeSetUpDevice(device, true); |
100 } | 133 } |
101 | 134 |
102 // UsbService::observer override. | 135 // UsbService::observer override. |
103 void OnDeviceRemoved(scoped_refptr<device::UsbDevice> device) override { | 136 void OnDeviceRemoved(scoped_refptr<device::UsbDevice> device) override { |
104 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 137 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
105 if (!UsbDeviceIsPrinter(*device)) { | 138 if (!UsbDeviceIsPrinter(*device)) { |
106 return; | 139 return; |
107 } | 140 } |
108 known_printers_.erase(device->guid()); | |
109 // TODO(justincarlson): Update failed printers. | |
110 } | |
111 | 141 |
112 // Returns the existing printer using this URI, if one exists, or | 142 base::AutoLock auto_lock(pp_lock_); |
113 // null otherwise. | 143 if (base::ContainsKey(present_printers_, device->guid())) { |
114 std::unique_ptr<Printer> FindExistingPrinter(const std::string& uri) { | 144 present_printers_.erase(device->guid()); |
115 // TODO(justincarlson): add a GetPrinterByURI to PrintersManager. | 145 auto printers = GetPrintersLocked(); |
116 // https://crbug.com/700602 | 146 // We already have pp_lock_, so need to call the pre-locked version of |
117 auto existing_printers = | 147 // GetPrinters to prevent deadlock. |
118 PrintersManagerFactory::GetForBrowserContext(profile_)->GetPrinters(); | 148 observer_list_->Notify( |
119 for (std::unique_ptr<Printer>& printer : existing_printers) { | 149 FROM_HERE, &PrinterDetector::Observer::OnAvailableUsbPrintersChanged, |
120 if (printer->uri() == uri) { | 150 GetPrintersLocked()); |
121 // Found a match, so use the existing configuration. | 151 } else { |
122 return std::move(printer); | 152 // If the device has been removed but it's not in present_printers_, it |
123 } | 153 // must still be in the setup flow. |
| 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 std::unique_ptr<Printer> existing_printer_configuration = |
| 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>(*data->printer); |
217 PrintersManagerFactory::GetForBrowserContext(profile_)->RegisterPrinter( | 258 PrintersManagerFactory::GetForBrowserContext(profile_)->RegisterPrinter( |
218 std::move(data->printer)); | 259 std::move(printer_copy)); |
219 } | 260 } |
220 // TODO(justincarlson): If the device was hotplugged, pop a timed | 261 // TODO(justincarlson): If the device was hotplugged, pop a timed |
221 // notification that says the printer is now available for printing. | 262 // notification that says the printer is now available for printing. |
222 } | 263 } |
223 // TODO(justincarlson): If this doesn't succeed, Pop a notification that | 264 // 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 | 265 // tells the user automatic setup failed and offers to open the CUPS printer |
225 // configuration settings. | 266 // configuration settings. |
226 // | 267 |
227 // TODO(justincarlson): If this doesn't succeed, Update the list of printers | 268 if (base::ContainsKey(deferred_printer_removals_, data->device->guid())) { |
228 // that failed to set up. | 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 } |
229 } | 279 } |
230 | 280 |
231 void SetNotificationUIManagerForTesting( | 281 void SetNotificationUIManagerForTesting( |
232 NotificationUIManager* manager) override { | 282 NotificationUIManager* manager) override { |
233 LOG(FATAL) << "Not implemented for CUPS"; | 283 LOG(FATAL) << "Not implemented for CUPS"; |
234 } | 284 } |
235 | 285 |
236 // USB GUIDs of printers we've already dealt with. There's an inherent race | 286 // Map from USB GUID to Printer that we have detected as being currently |
237 // between initially querying all usb devices and receiving a notification | 287 // 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 | 288 // accessed from multiple threads, so is protected by pp_lock_. |
239 // printer exactly once. | 289 std::map<std::string, std::unique_ptr<Printer>> present_printers_; |
240 std::set<std::string> known_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_; |
241 | 296 |
242 Profile* profile_; | 297 Profile* profile_; |
243 ScopedObserver<device::UsbService, device::UsbService::Observer> observer_; | 298 ScopedObserver<device::UsbService, device::UsbService::Observer> |
| 299 usb_observer_; |
| 300 scoped_refptr<base::ObserverListThreadSafe<PrinterDetector::Observer>> |
| 301 observer_list_; |
244 base::WeakPtrFactory<CupsPrinterDetectorImpl> weak_ptr_factory_; | 302 base::WeakPtrFactory<CupsPrinterDetectorImpl> weak_ptr_factory_; |
245 }; | 303 }; |
246 | 304 |
247 } // namespace | 305 } // namespace |
248 | 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 |
249 // static | 313 // static |
250 std::unique_ptr<PrinterDetector> PrinterDetector::CreateCups(Profile* profile) { | 314 std::unique_ptr<PrinterDetector> PrinterDetector::CreateCups(Profile* profile) { |
251 return base::MakeUnique<CupsPrinterDetectorImpl>(profile); | 315 return base::MakeUnique<CupsPrinterDetectorImpl>(profile); |
252 } | 316 } |
253 | 317 |
254 } // namespace chromeos | 318 } // namespace chromeos |
OLD | NEW |