| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/printing/cups_printers_manager.h" |
| |
| #include <map> |
| #include <utility> |
| |
| #include "ash/public/cpp/network_config_service.h" |
| #include "base/functional/bind.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/observer_list.h" |
| #include "base/scoped_observation.h" |
| #include "base/sequence_checker.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "chrome/browser/ash/printing/automatic_usb_printer_configurer.h" |
| #include "chrome/browser/ash/printing/cups_printer_status_creator.h" |
| #include "chrome/browser/ash/printing/enterprise_printers_provider.h" |
| #include "chrome/browser/ash/printing/oauth2/client_ids_database.h" |
| #include "chrome/browser/ash/printing/ppd_provider_factory.h" |
| #include "chrome/browser/ash/printing/ppd_resolution_tracker.h" |
| #include "chrome/browser/ash/printing/print_servers_policy_provider.h" |
| #include "chrome/browser/ash/printing/print_servers_provider.h" |
| #include "chrome/browser/ash/printing/printer_configurer.h" |
| #include "chrome/browser/ash/printing/printer_event_tracker.h" |
| #include "chrome/browser/ash/printing/printer_event_tracker_factory.h" |
| #include "chrome/browser/ash/printing/printer_info.h" |
| #include "chrome/browser/ash/printing/printers_map.h" |
| #include "chrome/browser/ash/printing/server_printers_provider.h" |
| #include "chrome/browser/ash/printing/synced_printers_manager.h" |
| #include "chrome/browser/ash/printing/synced_printers_manager_factory.h" |
| #include "chrome/browser/ash/printing/usb_printer_detector.h" |
| #include "chrome/browser/ash/printing/usb_printer_notification_controller.h" |
| #include "chrome/browser/ash/printing/zeroconf_printer_detector.h" |
| #include "chrome/browser/ash/scanning/zeroconf_scanner_detector.h" |
| #include "chrome/browser/ash/settings/cros_settings.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/common/pref_names.h" |
| #include "chromeos/printing/cups_printer_status.h" |
| #include "chromeos/printing/printing_constants.h" |
| #include "chromeos/printing/uri.h" |
| #include "chromeos/services/network_config/public/cpp/cros_network_config_observer.h" |
| #include "components/device_event_log/device_event_log.h" |
| #include "components/policy/policy_constants.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "components/prefs/pref_member.h" |
| #include "components/prefs/pref_service.h" |
| #include "mojo/public/cpp/bindings/receiver.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "printing/printer_query_result.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| namespace ash { |
| |
| bool IsIppUri(const chromeos::Uri& uri) { |
| return (uri.GetScheme() == chromeos::kIppScheme || |
| uri.GetScheme() == chromeos::kIppsScheme); |
| } |
| |
| // TODO(b/192467856) Remove this metric gathering by M99 |
| void SendScannerCountToUMA(std::unique_ptr<ZeroconfScannerDetector> detector) { |
| if (detector == nullptr) { |
| PRINTER_LOG(DEBUG) << "SendScannerCountToUMA detector was null"; |
| return; |
| } |
| const uint16_t num_scanners = detector->GetScanners().size(); |
| base::UmaHistogramCounts100("Scanning.NumDetectedScannersAtLogin", |
| num_scanners); |
| } |
| |
| namespace { |
| |
| using ::chromeos::CupsPrinterStatus; |
| using ::chromeos::PpdProvider; |
| using ::chromeos::Printer; |
| using ::chromeos::PrinterClass; |
| using ::printing::PrinterQueryResult; |
| |
| class CupsPrintersManagerImpl |
| : public CupsPrintersManager, |
| public EnterprisePrintersProvider::Observer, |
| public PrintServersManager::Observer, |
| public SyncedPrintersManager::Observer, |
| public chromeos::network_config::CrosNetworkConfigObserver { |
| public: |
| // Identifiers for each of the underlying PrinterDetectors this |
| // class observes. |
| enum DetectorIds { kUsbDetector, kZeroconfDetector, kPrintServerDetector }; |
| |
| CupsPrintersManagerImpl( |
| SyncedPrintersManager* synced_printers_manager, |
| std::unique_ptr<PrinterDetector> usb_detector, |
| std::unique_ptr<PrinterDetector> zeroconf_detector, |
| scoped_refptr<PpdProvider> ppd_provider, |
| std::unique_ptr<PrinterConfigurer> printer_configurer, |
| std::unique_ptr<UsbPrinterNotificationController> |
| usb_notification_controller, |
| std::unique_ptr<PrintServersManager> print_servers_manager, |
| std::unique_ptr<EnterprisePrintersProvider> enterprise_printers_provider, |
| PrinterEventTracker* event_tracker, |
| PrefService* pref_service) |
| : synced_printers_manager_(synced_printers_manager), |
| usb_detector_(std::move(usb_detector)), |
| zeroconf_detector_(std::move(zeroconf_detector)), |
| ppd_provider_(std::move(ppd_provider)), |
| usb_notification_controller_(std::move(usb_notification_controller)), |
| auto_usb_printer_configurer_(std::move(printer_configurer), |
| this, |
| usb_notification_controller_.get()), |
| print_servers_manager_(std::move(print_servers_manager)), |
| enterprise_printers_provider_(std::move(enterprise_printers_provider)), |
| event_tracker_(event_tracker) { |
| // Add the |auto_usb_printer_configurer_| as an observer. |
| AddObserver(&auto_usb_printer_configurer_); |
| |
| GetNetworkConfigService( |
| remote_cros_network_config_.BindNewPipeAndPassReceiver()); |
| |
| remote_cros_network_config_->AddObserver( |
| cros_network_config_observer_receiver_.BindNewPipeAndPassRemote()); |
| |
| // Prime the printer cache with the saved printers. |
| printers_.ReplacePrintersInClass( |
| PrinterClass::kSaved, synced_printers_manager_->GetSavedPrinters()); |
| synced_printers_manager_observation_.Observe(synced_printers_manager_); |
| |
| // Prime the printer cache with the enterprise printers (observer called |
| // immediately). |
| enterprise_printers_provider_observation_.Observe( |
| enterprise_printers_provider_.get()); |
| |
| // Callbacks may ensue immediately when the observer proxies are set up, so |
| // these instantiations must come after everything else is initialized. |
| usb_detector_->RegisterPrintersFoundCallback( |
| base::BindRepeating(&CupsPrintersManagerImpl::OnPrintersFound, |
| weak_ptr_factory_.GetWeakPtr(), kUsbDetector)); |
| OnPrintersFound(kUsbDetector, usb_detector_->GetPrinters()); |
| |
| zeroconf_detector_->RegisterPrintersFoundCallback( |
| base::BindRepeating(&CupsPrintersManagerImpl::OnPrintersFound, |
| weak_ptr_factory_.GetWeakPtr(), kZeroconfDetector)); |
| OnPrintersFound(kZeroconfDetector, zeroconf_detector_->GetPrinters()); |
| |
| // TODO(b/192467856) Remove this metric gathering by M99 |
| // Creates a ZeroconfScannerDetector, then logs the number of scanners |
| // detected after 5 minutes. |
| base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce(&SendScannerCountToUMA, |
| ZeroconfScannerDetector::Create()), |
| base::Minutes(5)); |
| |
| print_servers_manager_->AddObserver(this); |
| |
| user_printers_allowed_.Init(prefs::kUserPrintersAllowed, pref_service); |
| } |
| |
| ~CupsPrintersManagerImpl() override = default; |
| |
| // Public API function. |
| std::vector<Printer> GetPrinters(PrinterClass printer_class) const override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| if (!user_printers_allowed_.GetValue() && |
| printer_class != PrinterClass::kEnterprise) { |
| // If printers are disabled then simply return an empty vector. |
| LOG(WARNING) << "Attempting to retrieve printers when " |
| "UserPrintersAllowed is set to false"; |
| return {}; |
| } |
| |
| // Without user data there is not need to filter out non-enterprise or |
| // insecure printers so return all the printers in |printer_class|. |
| return printers_.Get(printer_class); |
| } |
| |
| // Public API function. |
| void SavePrinter(const Printer& printer) override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| if (!user_printers_allowed_.GetValue()) { |
| LOG(WARNING) << "SavePrinter() called when " |
| "UserPrintersAllowed is set to false"; |
| return; |
| } |
| synced_printers_manager_->UpdateSavedPrinter(printer); |
| // Note that we will rebuild our lists when we get the observer |
| // callback from |synced_printers_manager_|. |
| } |
| |
| // Public API function. |
| void RemoveSavedPrinter(const std::string& printer_id) override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| installed_printer_fingerprints_.erase(printer_id); |
| auto existing = synced_printers_manager_->GetPrinter(printer_id); |
| if (existing) { |
| event_tracker_->RecordPrinterRemoved(*existing); |
| } |
| synced_printers_manager_->RemoveSavedPrinter(printer_id); |
| // Note that we will rebuild our lists when we get the observer |
| // callback from |synced_printers_manager_|. |
| } |
| |
| // Public API function. |
| void AddObserver(CupsPrintersManager::Observer* observer) override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| observer_list_.AddObserver(observer); |
| if (enterprise_printers_are_ready_) { |
| observer->OnEnterprisePrintersInitialized(); |
| } |
| } |
| |
| // Public API function. |
| void RemoveObserver(CupsPrintersManager::Observer* observer) override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| observer_list_.RemoveObserver(observer); |
| } |
| |
| // Public API function. |
| void PrinterInstalled(const Printer& printer, bool is_automatic) override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| if (!user_printers_allowed_.GetValue()) { |
| LOG(WARNING) << "PrinterInstalled() called when " |
| "UserPrintersAllowed is set to false"; |
| return; |
| } |
| MaybeRecordInstallation(printer, is_automatic); |
| MarkPrinterInstalledWithCups(printer); |
| } |
| |
| // Public API function. |
| bool IsPrinterInstalled(const Printer& printer) const override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| const auto found = installed_printer_fingerprints_.find(printer.id()); |
| if (found == installed_printer_fingerprints_.end()) { |
| return false; |
| } |
| |
| return found->second == PrinterConfigurer::SetupFingerprint(printer); |
| } |
| |
| // Public API function. |
| void PrinterIsNotAutoconfigurable(const Printer& printer) override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| ppd_resolution_tracker_.MarkPrinterAsNotAutoconfigurable(printer.id()); |
| RebuildDetectedLists(); |
| } |
| |
| // Public API function. |
| absl::optional<Printer> GetPrinter(const std::string& id) const override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| if (!user_printers_allowed_.GetValue()) { |
| LOG(WARNING) << "UserPrintersAllowed is disabled - only searching " |
| "enterprise printers"; |
| return GetEnterprisePrinter(id); |
| } |
| |
| return printers_.Get(id); |
| } |
| |
| // SyncedPrintersManager::Observer implementation |
| void OnSavedPrintersChanged() override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| ResetNearbyPrintersLists(); |
| printers_.ReplacePrintersInClass( |
| PrinterClass::kSaved, synced_printers_manager_->GetSavedPrinters()); |
| RebuildDetectedLists(); |
| NotifyObservers({PrinterClass::kSaved}); |
| } |
| |
| // EnterprisePrintersProvider::Observer implementation |
| void OnPrintersChanged(bool complete, |
| const std::vector<Printer>& printers) override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| if (complete) { |
| printers_.ReplacePrintersInClass(PrinterClass::kEnterprise, printers); |
| if (!enterprise_printers_are_ready_) { |
| enterprise_printers_are_ready_ = true; |
| for (auto& observer : observer_list_) { |
| observer.OnEnterprisePrintersInitialized(); |
| } |
| } |
| } |
| NotifyObservers({PrinterClass::kEnterprise}); |
| } |
| |
| // CrosNetworkConfigObserver implementation. |
| void OnActiveNetworksChanged( |
| std::vector<chromeos::network_config::mojom::NetworkStatePropertiesPtr> |
| networks) override { |
| if (!HasNetworkDisconnected(networks)) { |
| // We only update the discovered list if we disconnected from our previous |
| // default network. |
| return; |
| } |
| |
| PRINTER_LOG(DEBUG) << "Network change. Refresh printers list."; |
| |
| // Clear the network detected printers when the active network changes. |
| // This ensures that connecting to a new network will give us only newly |
| // detected printers. |
| ClearNetworkDetectedPrinters(); |
| |
| // Notify observers that the printer list has changed. |
| RebuildDetectedLists(); |
| } |
| |
| // Callback for PrinterDetectors. |
| void OnPrintersFound( |
| int detector_id, |
| const std::vector<PrinterDetector::DetectedPrinter>& printers) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| switch (detector_id) { |
| case kUsbDetector: |
| usb_detections_ = printers; |
| break; |
| case kZeroconfDetector: |
| zeroconf_detections_ = printers; |
| break; |
| case kPrintServerDetector: |
| servers_detections_ = printers; |
| break; |
| } |
| RebuildDetectedLists(); |
| } |
| |
| // Callback for PrintServersManager. |
| void OnServerPrintersChanged( |
| const std::vector<PrinterDetector::DetectedPrinter>& printers) override { |
| OnPrintersFound(kPrintServerDetector, printers); |
| } |
| |
| void FetchPrinterStatus(const std::string& printer_id, |
| PrinterStatusCallback cb) override { |
| absl::optional<Printer> printer = GetPrinter(printer_id); |
| if (!printer) { |
| PRINTER_LOG(ERROR) << "Unable to complete printer status request. " |
| << "Printer not found. Printer id: " << printer_id; |
| CupsPrinterStatus printer_status(printer_id); |
| printer_status.AddStatusReason( |
| CupsPrinterStatus::CupsPrinterStatusReason::Reason:: |
| kPrinterUnreachable, |
| CupsPrinterStatus::CupsPrinterStatusReason::Severity::kError); |
| std::move(cb).Run(std::move(printer_status)); |
| return; |
| } |
| |
| // For USB printers, return NO ERROR if the printer is connected or PRINTER |
| // UNREACHABLE if the printer is disconnected. |
| if (printer->IsUsbProtocol()) { |
| CupsPrinterStatus printer_status(printer_id); |
| if (FindDetectedPrinter(printer_id)) { |
| printer_status.AddStatusReason( |
| CupsPrinterStatus::CupsPrinterStatusReason::Reason::kNoError, |
| CupsPrinterStatus::CupsPrinterStatusReason::Severity:: |
| kUnknownSeverity); |
| } else { |
| printer_status.AddStatusReason( |
| CupsPrinterStatus::CupsPrinterStatusReason::Reason:: |
| kPrinterUnreachable, |
| CupsPrinterStatus::CupsPrinterStatusReason::Severity::kError); |
| } |
| std::move(cb).Run(std::move(printer_status)); |
| return; |
| } |
| |
| // Behavior for querying a non-IPP uri is undefined and disallowed. |
| if (!IsIppUri(printer->uri())) { |
| PRINTER_LOG(ERROR) << "Unable to complete printer status request. " |
| << "Printer uri is invalid. Printer id: " |
| << printer_id; |
| CupsPrinterStatus printer_status(printer_id); |
| printer_status.AddStatusReason( |
| CupsPrinterStatus::CupsPrinterStatusReason::Reason::kUnknownReason, |
| CupsPrinterStatus::CupsPrinterStatusReason::Severity::kWarning); |
| std::move(cb).Run(std::move(printer_status)); |
| return; |
| } |
| |
| QueryIppPrinter( |
| printer->uri().GetHostEncoded(), printer->uri().GetPort(), |
| printer->uri().GetPathEncodedAsString(), |
| printer->uri().GetScheme() == chromeos::kIppsScheme, |
| base::BindOnce(&CupsPrintersManagerImpl::OnPrinterInfoFetched, |
| weak_ptr_factory_.GetWeakPtr(), printer_id, |
| std::move(cb))); |
| } |
| |
| // Public API function. |
| void RecordNearbyNetworkPrinterCounts() const override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| |
| size_t total_network_printers_count = zeroconf_detections_.size(); |
| // Count detected network printers that have not been saved |
| size_t nearby_zeroconf_printers_count = 0; |
| for (const PrinterDetector::DetectedPrinter& detected : |
| zeroconf_detections_) { |
| if (!printers_.IsPrinterInClass(PrinterClass::kSaved, |
| detected.printer.id())) { |
| ++nearby_zeroconf_printers_count; |
| } |
| } |
| |
| base::UmaHistogramCounts100("Printing.CUPS.TotalNetworkPrintersCount", |
| total_network_printers_count); |
| base::UmaHistogramCounts100("Printing.CUPS.NearbyNetworkPrintersCount", |
| nearby_zeroconf_printers_count); |
| } |
| |
| PrintServersManager* GetPrintServersManager() const override { |
| return print_servers_manager_.get(); |
| } |
| |
| // Callback for FetchPrinterStatus |
| void OnPrinterInfoFetched( |
| const std::string& printer_id, |
| PrinterStatusCallback cb, |
| PrinterQueryResult result, |
| const ::printing::PrinterStatus& printer_status, |
| const std::string& make_and_model, |
| const std::vector<std::string>& document_formats, |
| bool ipp_everywhere, |
| const chromeos::PrinterAuthenticationInfo& auth_info) { |
| SendPrinterStatus(printer_id, std::move(cb), result, printer_status, |
| auth_info); |
| } |
| |
| void SendPrinterStatus(const std::string& printer_id, |
| PrinterStatusCallback cb, |
| PrinterQueryResult result, |
| const ::printing::PrinterStatus& printer_status, |
| const chromeos::PrinterAuthenticationInfo& auth_info) { |
| base::UmaHistogramEnumeration("Printing.CUPS.PrinterStatusQueryResult", |
| result); |
| switch (result) { |
| case PrinterQueryResult::kHostnameResolution: |
| case PrinterQueryResult::kUnreachable: { |
| PRINTER_LOG(ERROR) |
| << "Printer status request failed. Could not reach printer " |
| << printer_id; |
| CupsPrinterStatus error_printer_status(printer_id); |
| error_printer_status.AddStatusReason( |
| CupsPrinterStatus::CupsPrinterStatusReason::Reason:: |
| kPrinterUnreachable, |
| CupsPrinterStatus::CupsPrinterStatusReason::Severity::kError); |
| std::move(cb).Run(std::move(error_printer_status)); |
| break; |
| } |
| case PrinterQueryResult::kUnknownFailure: { |
| PRINTER_LOG(ERROR) << "Printer status request failed. Unknown failure " |
| "trying to reach printer " |
| << printer_id; |
| CupsPrinterStatus error_printer_status(printer_id); |
| error_printer_status.AddStatusReason( |
| CupsPrinterStatus::CupsPrinterStatusReason::Reason::kUnknownReason, |
| CupsPrinterStatus::CupsPrinterStatusReason::Severity::kWarning); |
| std::move(cb).Run(std::move(error_printer_status)); |
| break; |
| } |
| case PrinterQueryResult::kSuccess: { |
| // Record results from PrinterStatus before converting to |
| // CupsPrinterStatus because the PrinterStatus enum contains more reason |
| // buckets. |
| for (const auto& reason : printer_status.reasons) { |
| base::UmaHistogramEnumeration("Printing.CUPS.PrinterStatusReasons", |
| reason.reason); |
| } |
| |
| // Convert printing::PrinterStatus to printing::CupsPrinterStatus |
| CupsPrinterStatus cups_printers_status = |
| PrinterStatusToCupsPrinterStatus(printer_id, printer_status, |
| auth_info); |
| |
| // Save the PrinterStatus so it can be attached along side future |
| // Printer retrievals. |
| printers_.SavePrinterStatus(printer_id, cups_printers_status); |
| |
| // Send status back to the handler through PrinterStatusCallback. |
| std::move(cb).Run(std::move(cups_printers_status)); |
| break; |
| } |
| } |
| } |
| |
| private: |
| absl::optional<Printer> GetEnterprisePrinter(const std::string& id) const { |
| return printers_.Get(PrinterClass::kEnterprise, id); |
| } |
| |
| // TODO(baileyberro): Remove the need for this function by pushing additional |
| // logic into PrintersMap. https://crbug.com/956172 |
| void ResetNearbyPrintersLists() { |
| printers_.Clear(PrinterClass::kAutomatic); |
| printers_.Clear(PrinterClass::kDiscovered); |
| } |
| |
| // Notify observers on the given classes the the relevant lists have changed. |
| void NotifyObservers(const std::vector<PrinterClass>& printer_classes) { |
| for (auto printer_class : printer_classes) { |
| auto printers = printers_.Get(printer_class); |
| PRINTER_LOG(DEBUG) << "Sending notification for " << printers.size() |
| << " printers in class (" << ToString(printer_class) |
| << ")"; |
| for (auto& observer : observer_list_) { |
| observer.OnPrintersChanged(printer_class, printers); |
| } |
| } |
| } |
| |
| // Look through all sources for the detected printer with the given id. |
| // Return a pointer to the printer on found, null if no entry is found. |
| const PrinterDetector::DetectedPrinter* FindDetectedPrinter( |
| const std::string& id) const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| for (const auto* printer_list : {&usb_detections_, &zeroconf_detections_}) { |
| for (const auto& detected : *printer_list) { |
| if (detected.printer.id() == id) { |
| return &detected; |
| } |
| } |
| } |
| return nullptr; |
| } |
| |
| void MaybeRecordInstallation(const Printer& printer, |
| bool is_automatic_installation) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| if (synced_printers_manager_->GetPrinter(printer.id())) { |
| // It's just an update, not a new installation, so don't record an event. |
| return; |
| } |
| |
| // For compatibility with the previous implementation, record USB printers |
| // separately from other IPP printers. Eventually we may want to shift |
| // this to be split by autodetected/not autodetected instead of USB/other |
| // IPP. |
| if (printer.IsUsbProtocol()) { |
| // Get the associated detection record if one exists. |
| const auto* detected = FindDetectedPrinter(printer.id()); |
| // We should have the full DetectedPrinter. We can't log the printer if |
| // we don't have it. |
| if (!detected) { |
| LOG(WARNING) << "Failed to find USB printer " << printer.id() |
| << " for installation event logging"; |
| return; |
| } |
| // For recording purposes, this is an automatic install if the ppd |
| // reference generated at detection time is the is the one we actually |
| // used -- i.e. the user didn't have to change anything to obtain a ppd |
| // that worked. |
| PrinterEventTracker::SetupMode mode; |
| if (is_automatic_installation) { |
| mode = PrinterEventTracker::kAutomatic; |
| } else { |
| mode = PrinterEventTracker::kUser; |
| } |
| event_tracker_->RecordUsbPrinterInstalled(*detected, mode); |
| } else { |
| PrinterEventTracker::SetupMode mode; |
| if (is_automatic_installation) { |
| mode = PrinterEventTracker::kAutomatic; |
| } else { |
| mode = PrinterEventTracker::kUser; |
| } |
| event_tracker_->RecordIppPrinterInstalled(printer, mode); |
| } |
| } |
| |
| void AddDetectedList( |
| const std::vector<PrinterDetector::DetectedPrinter>& detected_list) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| for (const PrinterDetector::DetectedPrinter& detected : detected_list) { |
| const std::string& detected_printer_id = detected.printer.id(); |
| if (printers_.IsPrinterInClass(PrinterClass::kSaved, |
| detected_printer_id)) { |
| // It's already in the saved class, don't need to do anything else here. |
| continue; |
| } |
| |
| // Sometimes the detector can flag a printer as IPP-everywhere compatible; |
| // those printers can go directly into the automatic class without further |
| // processing. |
| if (detected.printer.IsIppEverywhere()) { |
| printers_.Insert(PrinterClass::kAutomatic, detected.printer); |
| continue; |
| } |
| if (!ppd_resolution_tracker_.IsResolutionComplete(detected_printer_id)) { |
| // Didn't find an entry for this printer in the PpdReferences cache. We |
| // need to ask PpdProvider whether or not it can determine a |
| // PpdReference. If there's not already an outstanding request for one, |
| // start one. When the request comes back, we'll rerun classification |
| // and then should be able to figure out where this printer belongs. |
| if (!ppd_resolution_tracker_.IsResolutionPending(detected_printer_id)) { |
| ppd_resolution_tracker_.MarkResolutionPending(detected_printer_id); |
| ppd_provider_->ResolvePpdReference( |
| detected.ppd_search_data, |
| base::BindOnce(&CupsPrintersManagerImpl::ResolvePpdReferenceDone, |
| weak_ptr_factory_.GetWeakPtr(), |
| detected_printer_id)); |
| } |
| continue; |
| } |
| auto printer = detected.printer; |
| if (ppd_resolution_tracker_.WasResolutionSuccessful( |
| detected_printer_id)) { |
| // We have a ppd reference, so we think we can set this up |
| // automatically. |
| *printer.mutable_ppd_reference() = |
| ppd_resolution_tracker_.GetPpdReference(detected_printer_id); |
| printers_.Insert(PrinterClass::kAutomatic, printer); |
| continue; |
| } |
| if (!printer.supports_ippusb()) { |
| // Detected printer does not supports ipp-over-usb, so we cannot set it |
| // up automatically. We have to move it to the discovered class. |
| if (printer.IsUsbProtocol()) { |
| printer.set_usb_printer_manufacturer( |
| ppd_resolution_tracker_.GetManufacturer(detected_printer_id)); |
| } |
| printers_.Insert(PrinterClass::kDiscovered, printer); |
| continue; |
| } |
| // Detected printer supports ipp-over-usb and we could not find a ppd for |
| // it. We can try to set it up automatically (by IPP Everywhere). |
| if (ppd_resolution_tracker_.IsMarkedAsNotAutoconfigurable( |
| detected_printer_id)) { |
| // We have tried to autoconfigure the printer in the past and the |
| // process failed because of the lack of IPP Everywhere support. |
| // The printer must be treated as discovered printer. |
| printer.mutable_ppd_reference()->autoconf = false; |
| printers_.Insert(PrinterClass::kDiscovered, printer); |
| continue; |
| } |
| // We will try to autoconfigure the printer. We have to switch to |
| // the ippusb scheme. |
| printer.SetUri(chromeos::Uri( |
| base::StringPrintf("ippusb://%04x_%04x/ipp/print", |
| detected.ppd_search_data.usb_vendor_id, |
| detected.ppd_search_data.usb_product_id))); |
| printer.mutable_ppd_reference()->autoconf = true; |
| printers_.Insert(PrinterClass::kAutomatic, printer); |
| } |
| } |
| |
| // Returns true if we've disconnected from our current network. Updates |
| // the current active network. This method is not reentrant. |
| bool HasNetworkDisconnected( |
| const std::vector< |
| chromeos::network_config::mojom::NetworkStatePropertiesPtr>& |
| networks) { |
| // An empty current_network indicates that we're not connected to a valid |
| // network right now. |
| std::string current_network; |
| if (!networks.empty()) { |
| // The first network is the default network which receives mDNS |
| // multicasts. |
| current_network = networks.front()->guid; |
| } |
| |
| // If we attach to a network after being disconnected, we do not want to |
| // forcibly clear our detected list. It is either already empty or contains |
| // valid entries because we missed the original connection event. |
| bool network_disconnected = |
| !active_network_.empty() && current_network != active_network_; |
| |
| // Ensure that we don't register network state updates as network changes. |
| active_network_ = std::move(current_network); |
| |
| return network_disconnected; |
| } |
| |
| // Record in UMA the appropriate event with a setup attempt for a printer is |
| // abandoned. |
| void RecordSetupAbandoned(const Printer& printer) override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| if (printer.IsUsbProtocol()) { |
| const auto* detected = FindDetectedPrinter(printer.id()); |
| if (!detected) { |
| LOG(WARNING) << "Failed to find USB printer " << printer.id() |
| << " for abandoned event logging"; |
| return; |
| } |
| event_tracker_->RecordUsbSetupAbandoned(*detected); |
| } else { |
| event_tracker_->RecordSetupAbandoned(printer); |
| } |
| } |
| |
| // Rebuild the Automatic and Discovered printers lists from the (cached) raw |
| // detections. This will also generate OnPrintersChanged events for any |
| // observers observering either of the detected lists (kAutomatic and |
| // kDiscovered). |
| void RebuildDetectedLists() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| ResetNearbyPrintersLists(); |
| AddDetectedList(usb_detections_); |
| AddDetectedList(zeroconf_detections_); |
| AddDetectedList(servers_detections_); |
| NotifyObservers({PrinterClass::kAutomatic, PrinterClass::kDiscovered}); |
| } |
| |
| // Callback invoked on completion of PpdProvider::ResolvePpdReference. |
| void ResolvePpdReferenceDone(const std::string& printer_id, |
| PpdProvider::CallbackResultCode code, |
| const Printer::PpdReference& ref, |
| const std::string& usb_manufacturer) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| if (code == PpdProvider::SUCCESS) { |
| ppd_resolution_tracker_.MarkResolutionSuccessful(printer_id, ref); |
| } else { |
| LOG(WARNING) << "Failed to resolve PPD reference for " << printer_id; |
| ppd_resolution_tracker_.MarkResolutionFailed(printer_id); |
| if (!usb_manufacturer.empty()) { |
| ppd_resolution_tracker_.SetManufacturer(printer_id, usb_manufacturer); |
| } |
| } |
| RebuildDetectedLists(); |
| } |
| |
| // Records that |printer| has been installed in CUPS. |
| void MarkPrinterInstalledWithCups(const Printer& printer) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_); |
| installed_printer_fingerprints_[printer.id()] = |
| PrinterConfigurer::SetupFingerprint(printer); |
| } |
| |
| // Resets all network detected printer lists. |
| void ClearNetworkDetectedPrinters() { |
| PRINTER_LOG(DEBUG) << "Clear network printers"; |
| zeroconf_detections_.clear(); |
| |
| ResetNearbyPrintersLists(); |
| } |
| |
| SEQUENCE_CHECKER(sequence_); |
| |
| // Source lists for detected printers. |
| std::vector<PrinterDetector::DetectedPrinter> usb_detections_; |
| std::vector<PrinterDetector::DetectedPrinter> zeroconf_detections_; |
| std::vector<PrinterDetector::DetectedPrinter> servers_detections_; |
| |
| // Not owned. |
| SyncedPrintersManager* const synced_printers_manager_; |
| base::ScopedObservation<SyncedPrintersManager, |
| SyncedPrintersManager::Observer> |
| synced_printers_manager_observation_{this}; |
| mojo::Remote<chromeos::network_config::mojom::CrosNetworkConfig> |
| remote_cros_network_config_; |
| mojo::Receiver<chromeos::network_config::mojom::CrosNetworkConfigObserver> |
| cros_network_config_observer_receiver_{this}; |
| |
| std::unique_ptr<PrinterDetector> usb_detector_; |
| |
| std::unique_ptr<PrinterDetector> zeroconf_detector_; |
| |
| scoped_refptr<PpdProvider> ppd_provider_; |
| |
| std::unique_ptr<UsbPrinterNotificationController> |
| usb_notification_controller_; |
| |
| AutomaticUsbPrinterConfigurer auto_usb_printer_configurer_; |
| |
| std::unique_ptr<PrintServersManager> print_servers_manager_; |
| |
| std::unique_ptr<EnterprisePrintersProvider> enterprise_printers_provider_; |
| base::ScopedObservation<EnterprisePrintersProvider, |
| EnterprisePrintersProvider::Observer> |
| enterprise_printers_provider_observation_{this}; |
| |
| // Not owned |
| PrinterEventTracker* const event_tracker_; |
| |
| // Categorized printers. This is indexed by PrinterClass. |
| PrintersMap printers_; |
| |
| // Equals true if the list of enterprise printers and related policies |
| // is initialized and configured correctly. |
| bool enterprise_printers_are_ready_ = false; |
| |
| // GUID of the current default network. |
| std::string active_network_; |
| |
| // Tracks PpdReference resolution. Also stores USB manufacturer string if |
| // available. |
| PpdResolutionTracker ppd_resolution_tracker_; |
| |
| // Map of printer ids to PrinterConfigurer setup fingerprints at the time |
| // the printers was last installed with CUPS. |
| std::map<std::string, std::string> installed_printer_fingerprints_; |
| |
| base::ObserverList<CupsPrintersManager::Observer>::Unchecked observer_list_; |
| |
| // Holds the current value of the pref |UserPrintersAllowed|. |
| BooleanPrefMember user_printers_allowed_; |
| |
| base::WeakPtrFactory<CupsPrintersManagerImpl> weak_ptr_factory_{this}; |
| }; |
| |
| } // namespace |
| |
| // static |
| std::unique_ptr<CupsPrintersManager> CupsPrintersManager::Create( |
| Profile* profile) { |
| return std::make_unique<CupsPrintersManagerImpl>( |
| SyncedPrintersManagerFactory::GetInstance()->GetForBrowserContext( |
| profile), |
| UsbPrinterDetector::Create(), ZeroconfPrinterDetector::Create(), |
| CreatePpdProvider(profile), PrinterConfigurer::Create(profile), |
| UsbPrinterNotificationController::Create(profile), |
| PrintServersManager::Create(profile), |
| EnterprisePrintersProvider::Create(CrosSettings::Get(), profile), |
| PrinterEventTrackerFactory::GetInstance()->GetForBrowserContext(profile), |
| profile->GetPrefs()); |
| } |
| |
| // static |
| std::unique_ptr<CupsPrintersManager> CupsPrintersManager::CreateForTesting( |
| SyncedPrintersManager* synced_printers_manager, |
| std::unique_ptr<PrinterDetector> usb_detector, |
| std::unique_ptr<PrinterDetector> zeroconf_detector, |
| scoped_refptr<PpdProvider> ppd_provider, |
| std::unique_ptr<PrinterConfigurer> printer_configurer, |
| std::unique_ptr<UsbPrinterNotificationController> |
| usb_notification_controller, |
| std::unique_ptr<PrintServersManager> print_servers_manager, |
| std::unique_ptr<EnterprisePrintersProvider> enterprise_printers_provider, |
| PrinterEventTracker* event_tracker, |
| PrefService* pref_service) { |
| return std::make_unique<CupsPrintersManagerImpl>( |
| synced_printers_manager, std::move(usb_detector), |
| std::move(zeroconf_detector), std::move(ppd_provider), |
| std::move(printer_configurer), std::move(usb_notification_controller), |
| std::move(print_servers_manager), std::move(enterprise_printers_provider), |
| event_tracker, pref_service); |
| } |
| |
| // static |
| void CupsPrintersManager::RegisterProfilePrefs( |
| user_prefs::PrefRegistrySyncable* registry) { |
| registry->RegisterBooleanPref( |
| prefs::kUserPrintersAllowed, true, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterBooleanPref(prefs::kPrintingSendUsernameAndFilenameEnabled, |
| false); |
| PrintServersProvider::RegisterProfilePrefs(registry); |
| } |
| |
| // static |
| void CupsPrintersManager::RegisterLocalStatePrefs( |
| PrefRegistrySimple* registry) { |
| PrintServersProvider::RegisterLocalStatePrefs(registry); |
| printing::oauth2::ClientIdsDatabase::RegisterLocalStatePrefs(registry); |
| } |
| |
| } // namespace ash |