blob: cfecc54605f8dc634cfb051fcae5c77a1ab012df [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef LORGNETTE_DEVICE_TRACKER_H_
#define LORGNETTE_DEVICE_TRACKER_H_
#include <stdint.h>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include <base/containers/flat_map.h>
#include <base/functional/callback.h>
#include <base/functional/callback_helpers.h>
#include <base/memory/weak_ptr.h>
#include <base/sequence_checker.h>
#include <base/time/time.h>
#include <brillo/errors/error.h>
#include <lorgnette/proto_bindings/lorgnette_service.pb.h>
#include <metrics/metrics_library.h>
#include "lorgnette/dbus_adaptors/org.chromium.lorgnette.Manager.h"
using brillo::dbus_utils::DBusMethodResponse;
namespace brillo {
namespace dbus_utils {
class ExportedObjectManager;
} // namespace dbus_utils
} // namespace brillo
namespace lorgnette {
using ScannerListChangedSignalSender =
base::RepeatingCallback<void(const ScannerListChangedSignal&)>;
class FirewallManager;
class LibusbWrapper;
class SaneClient;
class UsbDevice;
// DeviceTracker is responsible for keeping track of which scanners are
// available and which ones are in use at any given time.
class DeviceTracker {
public:
DeviceTracker(SaneClient* sane_client, LibusbWrapper* libusb);
DeviceTracker(const DeviceTracker&) = delete;
DeviceTracker& operator=(const DeviceTracker&) = delete;
virtual ~DeviceTracker();
void SetScannerListChangedSignalSender(ScannerListChangedSignalSender sender);
void SetFirewallManager(FirewallManager* firewall_manager);
size_t NumActiveDiscoverySessions() const;
base::Time LastDiscoverySessionActivity() const;
// StartScannerDiscovery kicks off a new scanner discovery session. The
// session as a whole follows roughly these phases:
// 1. If network detection is requested, open the firewall ports.
// 2. If DLC policy is set to always download, start downloading the backend
// DLC in the background.
// 3. Enumerate USB devices
// 3a. If a device supports IPP-USB, post a task to probe it for eSCL
// support. If it supports eSCL, add an entry and send a scanner
// added signal.
// 3b. If a device requires a DLC backend and DLC isn't already
// downloading, start downloading the backend DLC in the background.
// 4. Wait for DLC to be downloaded and mounted as needed.
// 5. Enumerate the SANE devices
// 5a. For each device, post a task to try to match it to one of the
// previously found IPP-USB entries. Add a scanner entry and send
// a scanner added signal.
// 6. Send a "local enum complete" signal.
virtual StartScannerDiscoveryResponse StartScannerDiscovery(
const StartScannerDiscoveryRequest& request);
// StopScannerDiscovery closes an existing scanner discovery session.
// ScannerListChanged signals may continue to be sent if other sessions are
// still open.
virtual StopScannerDiscoveryResponse StopScannerDiscovery(
const StopScannerDiscoveryRequest& request);
private:
struct DiscoverySessionState {
std::string client_id;
base::Time start_time;
BackendDownloadPolicy dlc_policy;
bool dlc_started;
bool local_only;
};
std::optional<DiscoverySessionState*> GetSession(
const std::string& session_id);
// Individual phases of discovery. Each function is posted as a separate task
// on the event loop to break up the amount of time spent blocking.
// Because other events can be processed in between, each function needs to
// re-check if its `session_id` still refers to an active session before doing
// any processing.
void StartDiscoverySessionInternal(std::string session_id);
void EnumerateUSBDevices(std::string session_id);
void ProbeIPPUSBDevice(std::string session_id,
std::unique_ptr<UsbDevice> device);
void EnumerateSANEDevices(std::string session_id);
void ProbeSANEDevice(std::string session_id);
void SendEnumerationCompletedSignal(std::string session_id);
void SendSessionEndingSignal(std::string session_id);
SEQUENCE_CHECKER(sequence_checker_);
// Manages connection to SANE for listing and connecting to scanners.
// Not owned.
SaneClient* sane_client_ GUARDED_BY_CONTEXT(sequence_checker_);
// Manages port access for receiving replies from network scanners.
// Not owned.
FirewallManager* firewall_manager_ GUARDED_BY_CONTEXT(sequence_checker_);
// Manages a libusb context for querying USB devices.
// Not owned.
LibusbWrapper* libusb_ GUARDED_BY_CONTEXT(sequence_checker_);
// All the previously discovered devices. Used to match subsequent SANE
// entries for the same device and to accelerate subsequent discovery
// sessions.
std::vector<ScannerInfo> known_devices_ GUARDED_BY_CONTEXT(sequence_checker_);
// A callback to call when we attempt to send a D-Bus signal. This is used
// for testing in order to track the signals sent from StartScan.
ScannerListChangedSignalSender signal_sender_;
// Mapping from discovery session IDs to session state.
// The session_id is passed around instead of a pointer to avoid creating
// dangling pointers if a session is closed while discovery events are
// pending.
base::flat_map<std::string, DiscoverySessionState> discovery_sessions_
GUARDED_BY_CONTEXT(sequence_checker_);
// Keep as the last member variable.
base::WeakPtrFactory<DeviceTracker> weak_factory_
GUARDED_BY_CONTEXT(sequence_checker_){this};
};
} // namespace lorgnette
#endif // LORGNETTE_DEVICE_TRACKER_H_