blob: cfb4ee93d28b5d9784df41146e5aa6dd8c0804f8 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdint.h>
#include <map>
#include <memory>
#include <set>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/observer_list_threadsafe.h"
#include "base/scoped_observer.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/printing/ppd_provider_factory.h"
#include "chrome/browser/chromeos/printing/printer_configurer.h"
#include "chrome/browser/chromeos/printing/printer_event_tracker.h"
#include "chrome/browser/chromeos/printing/printer_event_tracker_factory.h"
#include "chrome/browser/chromeos/printing/synced_printers_manager_factory.h"
#include "chrome/browser/chromeos/printing/usb_printer_detector.h"
#include "chrome/browser/chromeos/printing/usb_printer_util.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/debug_daemon_client.h"
#include "chromeos/printing/ppd_provider.h"
#include "content/public/browser/browser_thread.h"
#include "device/base/device_client.h"
#include "device/usb/usb_device.h"
#include "device/usb/usb_service.h"
namespace chromeos {
namespace {
// Given a usb device, guesses the make and model for a driver lookup.
//
// TODO(justincarlson): Possibly go deeper and query the IEEE1284 fields
// for make and model if we determine those are more likely to contain
// what we want. Strings currently come from udev.
std::string GuessEffectiveMakeAndModel(const device::UsbDevice& device) {
return base::UTF16ToUTF8(device.manufacturer_string()) + " " +
base::UTF16ToUTF8(device.product_string());
}
// The PrinterDetector that drives the flow for setting up a USB printer to use
// CUPS backend.
class UsbPrinterDetectorImpl : public UsbPrinterDetector,
public device::UsbService::Observer {
public:
UsbPrinterDetectorImpl(device::UsbService* usb_service)
: usb_observer_(this),
observer_list_(
new base::ObserverListThreadSafe<UsbPrinterDetector::Observer>),
weak_ptr_factory_(this) {
if (usb_service) {
usb_observer_.Add(usb_service);
usb_service->GetDevices(base::Bind(&UsbPrinterDetectorImpl::OnGetDevices,
weak_ptr_factory_.GetWeakPtr()));
}
}
~UsbPrinterDetectorImpl() override = default;
// PrinterDetector interface function.
void AddObserver(UsbPrinterDetector::Observer* observer) override {
observer_list_->AddObserver(observer);
}
// PrinterDetector interface function.
void RemoveObserver(UsbPrinterDetector::Observer* observer) override {
observer_list_->RemoveObserver(observer);
}
void StartObservers() override {
// This is subtle. We're scheduling a callback here, so we have to be
// careful about cancellation. We can't just post the task to the sequence,
// because there's the possibility the observer will be deleted before the
// task executes. We can't call the callbacks directly here because the
// common usage of observers involves having the observer object not be
// fully constructed when AddObserver is called.
//
// The easiest way to deal with this is to just let ThreadSafeObserverList
// sort it out. It already handles the removed-before-callback-executes
// case, so makes things safe for us. This means we may *over*-notify a
// bit, but since the number of observers is expected to be small (usually
// just 1) that's a price we're willing to pay.
observer_list_->Notify(
FROM_HERE, &PrinterDetector::Observer::OnPrintersFound, GetPrinters());
// In the case of the USB detector, we expect to always have up-to-date
// information, thus the scan is complete after the first OnPrintersFound
// callback.
observer_list_->Notify(FROM_HERE,
&PrinterDetector::Observer::OnPrinterScanComplete);
}
// PrinterDetector interface function.
std::vector<DetectedPrinter> GetPrinters() override {
base::AutoLock auto_lock(printers_lock_);
return GetPrintersLocked();
}
private:
std::vector<DetectedPrinter> GetPrintersLocked() {
printers_lock_.AssertAcquired();
std::vector<DetectedPrinter> printers_list;
printers_list.reserve(printers_.size());
for (const auto& entry : printers_) {
printers_list.push_back(entry.second);
}
return printers_list;
}
// Callback for initial enumeration of usb devices.
void OnGetDevices(
const std::vector<scoped_refptr<device::UsbDevice>>& devices) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
for (const auto& device : devices) {
OnDeviceAdded(device);
}
}
// UsbService::observer override.
void OnDeviceAdded(scoped_refptr<device::UsbDevice> device) override {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!UsbDeviceIsPrinter(*device)) {
return;
}
std::unique_ptr<Printer> converted = UsbDeviceToPrinter(*device);
if (!converted.get()) {
// An error will already have been logged if we failed to convert.
return;
}
DetectedPrinter entry;
entry.printer = *converted;
entry.ppd_search_data.usb_vendor_id = device->vendor_id();
entry.ppd_search_data.usb_product_id = device->product_id();
entry.ppd_search_data.make_and_model.push_back(
GuessEffectiveMakeAndModel(*device));
base::AutoLock auto_lock(printers_lock_);
printers_[device->guid()] = entry;
observer_list_->Notify(FROM_HERE,
&PrinterDetector::Observer::OnPrintersFound,
GetPrintersLocked());
}
// UsbService::observer override.
void OnDeviceRemoved(scoped_refptr<device::UsbDevice> device) override {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!UsbDeviceIsPrinter(*device)) {
return;
}
base::AutoLock auto_lock(printers_lock_);
printers_.erase(device->guid());
observer_list_->Notify(FROM_HERE,
&PrinterDetector::Observer::OnPrintersFound,
GetPrintersLocked());
}
// Map from USB GUID to DetectedPrinter for all detected printers, and
// associated lock, since we don't require all access to be from the same
// sequence.
std::map<std::string, DetectedPrinter> printers_;
base::Lock printers_lock_;
ScopedObserver<device::UsbService, device::UsbService::Observer>
usb_observer_;
scoped_refptr<base::ObserverListThreadSafe<UsbPrinterDetector::Observer>>
observer_list_;
base::WeakPtrFactory<UsbPrinterDetectorImpl> weak_ptr_factory_;
};
} // namespace
// static
std::unique_ptr<UsbPrinterDetector> UsbPrinterDetector::Create() {
device::UsbService* usb_service =
device::DeviceClient::Get()->GetUsbService();
return base::MakeUnique<UsbPrinterDetectorImpl>(usb_service);
}
std::unique_ptr<UsbPrinterDetector> UsbPrinterDetector::CreateForTesting(
device::UsbService* usb_service) {
return base::MakeUnique<UsbPrinterDetectorImpl>(usb_service);
}
} // namespace chromeos